diff --git a/README.md b/README.md index e955e80..931fb64 100644 --- a/README.md +++ b/README.md @@ -1 +1,37 @@ -# mini-wp \ No newline at end of file +# Mini Wordpress + +Mini Wordpress project by Arief Rachman + + +>### API Endpoints for Article +| Route | Method | Desc | +|-------|--------|------| +| /articles/discover | **GET** | Read all Articles members myWordpress **(readonly, allusers)** | +| /articles/myArticles | **GET** | Edit and delete own articles **(owner article)** | +| /articles/ | **POST** | Create a new article **(member only)** | +| /articles/ | **PUT** | Edit article **(member only, owner article)** | +| /articles/ | **DELETE** | Delete article **(member only, owner article)** | + +>### API Endpoints for Article +| Route | Method | Desc | +|-------|--------|------| +| /user/signup | **POST** | Register as new member | +| /user/signin | **POST** | Login into myWordpress | +| /user/auth | **GET** | Authentication user | + +>### Usage Guide +Before you run this app, make sure you have : +1. Installed Node.js and npm installed in your computer +2. Google Cloud Platform Account +3. Bucket in your Google Cloud Storage +4. Fill the .env-template +5. Get your service credentials account and download the private key in `JSON` format. For doing so, you can follow this [documentation](https://cloud.google.com/storage/docs/authentication#service_accounts) or this [youtube video](https://www.youtube.com/watch?v=tSnzoW4RlaQ) and move it into project directory. +6. and run these commands +```bash +$ npm install +$ npm start +``` + +>### Deploy Link + +__coming soon__ \ No newline at end of file diff --git a/client/css/style.css b/client/css/style.css new file mode 100644 index 0000000..2a2adbd --- /dev/null +++ b/client/css/style.css @@ -0,0 +1,67 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat'); + +.container { + margin-left: 0px !important; +} + +.sidebar { + padding: 0px !important; +} + +.sidebar h4 { + font-size: 16px; + font-weight: bold; + padding: 10px; + color: #777a7c; +} + +ul { + padding: 0; + list-style: none; +} + +.sidebar li { + padding: 10px; + cursor: pointer; +} + +.sidebar li:hover { + background-color: #bec3c8; +} + +.sidebar span { + font-family: 'Acme', sans-serif; + font-weight: 200; + margin-left: 10px; +} + +.article { + max-width: 610px; + margin: 0 auto; + padding: 60px 30px 90px; +} + +.header .desc { + font-family: 'Montserrat', sans-serif; + color: #7b8994; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 1.342em; + line-height: 1; +} +.header h1 { + font-family: 'Montserrat', sans-serif; + font-weight: bold; + font-size: 2.415em; + line-height: 1.2; + letter-spacing: -1px; + padding-bottom: 6px; + border-bottom: 2px solid gray; + */ +} + +.read-article p { + margin-top: 2em; + margin-bottom: 1em; + line-height: 2em; +} \ No newline at end of file diff --git a/client/css/vueWysiwyg.css b/client/css/vueWysiwyg.css new file mode 100644 index 0000000..e44749e --- /dev/null +++ b/client/css/vueWysiwyg.css @@ -0,0 +1,596 @@ +.editr { + border: 1px solid #e4e4e4; + width: 100%; +} +.editr--toolbar { + background: #f6f6f6; + border-bottom: 1px solid #e4e4e4; + position: relative; + display: flex; + height: 32px; +} +.editr--toolbar a { + display: inline-block; + width: 8vw; + max-width: 32px; + height: 32px; + color: #333; + fill: #333; + cursor: pointer; + text-align: center; + line-height: 1; +} +.editr--toolbar a:hover { + background: rgba(0,0,0,0.1); +} +.editr--toolbar a:active { + background: rgba(0,0,0,0.2); +} +.editr--toolbar a svg { + width: 16px; + height: 16px; + margin: 8px auto; +} +.editr--toolbar a svg path { + fill: inherit; +} +.editr--toolbar a.vw-btn-separator { + width: 1px; + margin: 0 8px; +} +.editr--toolbar a.vw-btn-separator:hover { + background: initial; + cursor: default; +} +.editr--toolbar a.vw-btn-separator i.vw-separator { + border-left: 1px solid rgba(0,0,0,0.1); + height: 100%; + position: absolute; + width: 1px; +} +.editr--toolbar .dashboard { + width: 100%; + position: absolute; + top: 32px; + left: 0; + text-align: left; + padding: 8px 16px; + background: rgba(255,255,255,0.95); + border: 1px solid #f6f6f6; +} +.editr--content { + min-height: 150px; + padding: 12px 8px 16px 8px; + line-height: 1.33; + font-family: inherit; + color: inherit; + overflow-y: auto; +} +.editr--content[contenteditable=true]:empty:before { + content: attr(placeholder); + color: rgba(0,0,0,0.3); + display: block; /* For Firefox */ +} +.editr--content img { + max-width: 100%; +} +.editr--content table { + width: 100%; + border-collapse: collapse; +} +.editr--content table th { + text-align: left; +} +.editr--content table th, +.editr--content table td { + border: 1px solid #ddd; + padding: 2px; +} +.editr--content:focus { + outline: 0; +} +.editr--content ul li, +.editr--content ol li { + list-style-position: inside; +} +@media screen and (max-width: 320px) { + .editr--toolbar a { + margin: 0 2px; + } + .editr--toolbar a.vw-btn-separator { + display: none; + } +}/* + * The MIT License + * Copyright (c) 2012 Matias Meno + */ + @-webkit-keyframes passing-through { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30%, 70% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } + 100% { + opacity: 0; + -webkit-transform: translateY(-40px); + -moz-transform: translateY(-40px); + -ms-transform: translateY(-40px); + -o-transform: translateY(-40px); + transform: translateY(-40px); } } + @-moz-keyframes passing-through { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30%, 70% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } + 100% { + opacity: 0; + -webkit-transform: translateY(-40px); + -moz-transform: translateY(-40px); + -ms-transform: translateY(-40px); + -o-transform: translateY(-40px); + transform: translateY(-40px); } } + @keyframes passing-through { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30%, 70% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } + 100% { + opacity: 0; + -webkit-transform: translateY(-40px); + -moz-transform: translateY(-40px); + -ms-transform: translateY(-40px); + -o-transform: translateY(-40px); + transform: translateY(-40px); } } + @-webkit-keyframes slide-in { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } } + @-moz-keyframes slide-in { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } } + @keyframes slide-in { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } } + @-webkit-keyframes pulse { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } + 10% { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); } + 20% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } } + @-moz-keyframes pulse { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } + 10% { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); } + 20% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } } + @keyframes pulse { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } + 10% { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); } + 20% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } } + .dropzone, .dropzone * { + box-sizing: border-box; } + + .dropzone { + min-height: 150px; + border: 2px solid rgba(0, 0, 0, 0.3); + background: white; + padding: 20px 20px; } + .dropzone.dz-clickable { + cursor: pointer; } + .dropzone.dz-clickable * { + cursor: default; } + .dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * { + cursor: pointer; } + .dropzone.dz-started .dz-message { + display: none; } + .dropzone.dz-drag-hover { + border-style: solid; } + .dropzone.dz-drag-hover .dz-message { + opacity: 0.5; } + .dropzone .dz-message { + text-align: center; + margin: 2em 0; } + .dropzone .dz-preview { + position: relative; + display: inline-block; + vertical-align: top; + margin: 16px; + min-height: 100px; } + .dropzone .dz-preview:hover { + z-index: 1000; } + .dropzone .dz-preview:hover .dz-details { + opacity: 1; } + .dropzone .dz-preview.dz-file-preview .dz-image { + border-radius: 20px; + background: #999; + background: linear-gradient(to bottom, #eee, #ddd); } + .dropzone .dz-preview.dz-file-preview .dz-details { + opacity: 1; } + .dropzone .dz-preview.dz-image-preview { + background: white; } + .dropzone .dz-preview.dz-image-preview .dz-details { + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; } + .dropzone .dz-preview .dz-remove { + font-size: 14px; + text-align: center; + display: block; + cursor: pointer; + border: none; } + .dropzone .dz-preview .dz-remove:hover { + text-decoration: underline; } + .dropzone .dz-preview:hover .dz-details { + opacity: 1; } + .dropzone .dz-preview .dz-details { + z-index: 20; + position: absolute; + top: 0; + left: 0; + opacity: 0; + font-size: 13px; + min-width: 100%; + max-width: 100%; + padding: 2em 1em; + text-align: center; + color: rgba(0, 0, 0, 0.9); + line-height: 150%; } + .dropzone .dz-preview .dz-details .dz-size { + margin-bottom: 1em; + font-size: 16px; } + .dropzone .dz-preview .dz-details .dz-filename { + white-space: nowrap; } + .dropzone .dz-preview .dz-details .dz-filename:hover span { + border: 1px solid rgba(200, 200, 200, 0.8); + background-color: rgba(255, 255, 255, 0.8); } + .dropzone .dz-preview .dz-details .dz-filename:not(:hover) { + overflow: hidden; + text-overflow: ellipsis; } + .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span { + border: 1px solid transparent; } + .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span { + background-color: rgba(255, 255, 255, 0.4); + padding: 0 0.4em; + border-radius: 3px; } + .dropzone .dz-preview:hover .dz-image img { + -webkit-transform: scale(1.05, 1.05); + -moz-transform: scale(1.05, 1.05); + -ms-transform: scale(1.05, 1.05); + -o-transform: scale(1.05, 1.05); + transform: scale(1.05, 1.05); + -webkit-filter: blur(8px); + filter: blur(8px); } + .dropzone .dz-preview .dz-image { + border-radius: 20px; + overflow: hidden; + width: 120px; + height: 120px; + position: relative; + display: block; + z-index: 10; } + .dropzone .dz-preview .dz-image img { + display: block; } + .dropzone .dz-preview.dz-success .dz-success-mark { + -webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + -moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + -ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + -o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); } + .dropzone .dz-preview.dz-error .dz-error-mark { + opacity: 1; + -webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + -moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + -ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + -o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); } + .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark { + pointer-events: none; + opacity: 0; + z-index: 500; + position: absolute; + display: block; + top: 50%; + left: 50%; + margin-left: -27px; + margin-top: -27px; } + .dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg { + display: block; + width: 54px; + height: 54px; } + .dropzone .dz-preview.dz-processing .dz-progress { + opacity: 1; + -webkit-transition: all 0.2s linear; + -moz-transition: all 0.2s linear; + -ms-transition: all 0.2s linear; + -o-transition: all 0.2s linear; + transition: all 0.2s linear; } + .dropzone .dz-preview.dz-complete .dz-progress { + opacity: 0; + -webkit-transition: opacity 0.4s ease-in; + -moz-transition: opacity 0.4s ease-in; + -ms-transition: opacity 0.4s ease-in; + -o-transition: opacity 0.4s ease-in; + transition: opacity 0.4s ease-in; } + .dropzone .dz-preview:not(.dz-processing) .dz-progress { + -webkit-animation: pulse 6s ease infinite; + -moz-animation: pulse 6s ease infinite; + -ms-animation: pulse 6s ease infinite; + -o-animation: pulse 6s ease infinite; + animation: pulse 6s ease infinite; } + .dropzone .dz-preview .dz-progress { + opacity: 1; + z-index: 1000; + pointer-events: none; + position: absolute; + height: 16px; + left: 50%; + top: 50%; + margin-top: -8px; + width: 80px; + margin-left: -40px; + background: rgba(255, 255, 255, 0.9); + -webkit-transform: scale(1); + border-radius: 8px; + overflow: hidden; } + .dropzone .dz-preview .dz-progress .dz-upload { + background: #333; + background: linear-gradient(to bottom, #666, #444); + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 0; + -webkit-transition: width 300ms ease-in-out; + -moz-transition: width 300ms ease-in-out; + -ms-transition: width 300ms ease-in-out; + -o-transition: width 300ms ease-in-out; + transition: width 300ms ease-in-out; } + .dropzone .dz-preview.dz-error .dz-error-message { + display: block; } + .dropzone .dz-preview.dz-error:hover .dz-error-message { + opacity: 1; + pointer-events: auto; } + .dropzone .dz-preview .dz-error-message { + pointer-events: none; + z-index: 1000; + position: absolute; + display: block; + display: none; + opacity: 0; + -webkit-transition: opacity 0.3s ease; + -moz-transition: opacity 0.3s ease; + -ms-transition: opacity 0.3s ease; + -o-transition: opacity 0.3s ease; + transition: opacity 0.3s ease; + border-radius: 8px; + font-size: 13px; + top: 130px; + left: -10px; + width: 140px; + background: #be2626; + background: linear-gradient(to bottom, #be2626, #a92222); + padding: 0.5em 1.2em; + color: white; } + .dropzone .dz-preview .dz-error-message:after { + content: ''; + position: absolute; + top: -6px; + left: 64px; + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #be2626; } + + .vue-dropzone { + border: 2px solid #E5E5E5; + font-family: 'Arial', sans-serif; + letter-spacing: 0.2px; + color: #777; + transition: background-color 0.2s linear; + } + .vue-dropzone:hover { + background-color: #F6F6F6; + } + .vue-dropzone i { + color: #CCC; + } + .vue-dropzone .dz-preview .dz-image { + border-radius: 0; + width: 100%; + height: 100%; + } + .vue-dropzone .dz-preview .dz-image img:not([src]) { + width: 200px; + height: 200px; + } + .vue-dropzone .dz-preview .dz-image:hover img { + transform: none; + -webkit-filter: none; + } + .vue-dropzone .dz-preview .dz-details { + bottom: 0; + top: 0; + color: white; + background-color: rgba(33, 150, 243, 0.8); + transition: opacity .2s linear; + text-align: left; + } + .vue-dropzone .dz-preview .dz-details .dz-filename { + overflow: hidden; + } + .vue-dropzone .dz-preview .dz-details .dz-filename span, + .vue-dropzone .dz-preview .dz-details .dz-size span { + background-color: transparent; + } + .vue-dropzone .dz-preview .dz-details .dz-filename:not(:hover) span { + border: none; + } + .vue-dropzone .dz-preview .dz-details .dz-filename:hover span { + background-color: transparent; + border: none; + } + .vue-dropzone .dz-preview .dz-progress .dz-upload { + background: #cccccc; + } + .vue-dropzone .dz-preview .dz-remove { + position: absolute; + z-index: 30; + color: white; + margin-left: 15px; + padding: 10px; + top: inherit; + bottom: 15px; + border: 2px white solid; + text-decoration: none; + text-transform: uppercase; + font-size: 0.8rem; + font-weight: 800; + letter-spacing: 1.1px; + opacity: 0; + } + .vue-dropzone .dz-preview:hover .dz-remove { + opacity: 1; + } + .vue-dropzone .dz-preview .dz-success-mark, + .vue-dropzone .dz-preview .dz-error-mark { + margin-left: auto; + margin-top: auto; + width: 100%; + top: 35%; + left: 0; + } + .vue-dropzone .dz-preview .dz-success-mark svg, + .vue-dropzone .dz-preview .dz-error-mark svg { + margin-left: auto; + margin-right: auto; + } + .vue-dropzone .dz-preview .dz-error-message { + top: calc(15%); + margin-left: auto; + margin-right: auto; + left: 0; + width: 100%; + } + .vue-dropzone .dz-preview .dz-error-message:after { + bottom: -6px; + top: initial; + border-top: 6px solid #a92222; + border-bottom: none; + } + + + .form[data-v-ebce4d12] { + display: flex; + align-content: flex-end; + } + .form label[data-v-ebce4d12] { + margin-right: 1rem; + } diff --git a/client/img/logo-miniwp.png b/client/img/logo-miniwp.png new file mode 100644 index 0000000..9f78e59 Binary files /dev/null and b/client/img/logo-miniwp.png differ diff --git a/client/img/nav-logo.png b/client/img/nav-logo.png new file mode 100644 index 0000000..c6637aa Binary files /dev/null and b/client/img/nav-logo.png differ diff --git a/client/img/undraw_blog_anyj.png b/client/img/undraw_blog_anyj.png new file mode 100644 index 0000000..8ff29e8 Binary files /dev/null and b/client/img/undraw_blog_anyj.png differ diff --git a/client/img/undraw_file_searching_duff.png b/client/img/undraw_file_searching_duff.png new file mode 100644 index 0000000..b914120 Binary files /dev/null and b/client/img/undraw_file_searching_duff.png differ diff --git a/client/img/undraw_world_9iqb.png b/client/img/undraw_world_9iqb.png new file mode 100644 index 0000000..97cd2d0 Binary files /dev/null and b/client/img/undraw_world_9iqb.png differ diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..d5e5cd3 --- /dev/null +++ b/client/index.html @@ -0,0 +1,423 @@ + + + + + + + + + + + + + + + + + + + My Wordpress + + + + +
+ + + + +
+
+ + + + + +
+ + + + + + +
+ +
+ +

Create new article

+

Post your first article on My WordPress

+
+
+
+
+ Title : + + Description : + + +
+
+ +
+
+ +
+
+
+
+ + +
+
+
+ +
+
+

+ Discover Article around The Worlds +

+
+
+ + + + +
+ +
+ +
+ +
+ +
+ + +
+
+
+ +
+
{{ data.description}}
+

{{ data.created_at.slice(0, 10) }}

+
+ + +

+ {{ data.title }} +

+

+ + Author: {{ data.author.name }} +

+

+ +

+
+ +
+ +
+
+ +
+ + +
+ +
+
+ +
+
+

+ Search MyArticle +

+
+
+ + + + + +
+ +
+ +
+ +
+ +
+ + +
+
+
+ +
+
{{ data.description}}
+

{{ data.created_at.slice(0, 10) }}

+
+ + +

+ {{ data.title }} + +

+

+ Author: {{ data.author.name }} +

+

+ +

+
+ + + + +
+
+ +
+ +
+
+ +
+ + +
+ +
+ +

Edit Article

+ +
+
+
+
+ Title : + + Description : + +
+
+ +
+
+ +
+
+
+
+ + +
+
+
+
{{ readArt.description }}
+

{{ readArt.title }}

+
+ by {{ readArt.author && readArt.author.name }} +
+
+

+
+
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/js/main.js b/client/js/main.js new file mode 100644 index 0000000..fd2b121 --- /dev/null +++ b/client/js/main.js @@ -0,0 +1,239 @@ +var app = new Vue({ + el: '#app', + components: { + wysiwyg: vueWysiwyg.default.component, + }, + data: { + // ---- signin/signup ---- // + isLogin: false, + emailSignIn: '', + passwordSignIn: '', + nameSignUp: '', + emailSignUp: '', + passwordSignUp: '', + // ---- display sidebar ---- // + discoverEvents: true, + createArticleEvents: false, + myArticleEvents: false, + // ---- display content ---- // + showFormEdit: false, + showArticles: false, + // ---- list articles ---- // + discoverArticles: [], + ownArticles: [], + //---- data articles ---- // + id: '', + title: '', + description: '', + text: '', + author: '', + qSearch: '', + qOwnSearch: '', + readArt: {}, + file: '' + }, + methods: { + signUp: function () { + axios({ + method: 'POST', + url: 'http://localhost:3000/user/signup', + data: { + name: this.nameSignUp, + email: this.emailSignUp, + password: this.passwordSignUp + } + }) + .then((response) => { + console.log(response.data.result.message); + this.nameSignUp = ''; + this.emailSignUp = ''; + this.passwordSignUp = ''; + }) + .catch((err) => { + console.log(err); + }); + }, + signIn: function () { + axios({ + method: 'POST', + url: 'http://localhost:3000/user/signin', + data: { + email: this.emailSignIn, + password: this.passwordSignIn + } + }) + .then((response) => { + console.log(response.data.result.message); + localStorage.setItem('token', response.data.result.access_token); + this.isLogin = true; + this.myArticles(); + this.emailSignIn = ''; + this.passwordSignIn = ''; + }).catch((err) => { + console.log(err); + }); + }, + checkUser: function () { + axios({ + method: 'GET', + url: 'http://localhost:3000/user/auth', + headers: { token: localStorage.getItem('token') } + }) + .then((response) => { + this.isLogin = true; + }) + .catch((err) => { + console.log(err); + }); + }, + signOut: function () { + localStorage.removeItem('token'); + this.isLogin = false; + + }, + clearValue: function () { + this.title = ''; + this.description = ''; + this.text = ''; + }, + allArticles: function () { + axios({ + method: 'GET', + url: 'http://localhost:3000/articles/discover' + }) + .then(({ data }) => { + this.discoverArticles = data.result; + }) + .catch((err) => { + console.log(err); + }); + }, + myArticles: function () { + axios({ + method: 'GET', + url: 'http://localhost:3000/articles/myArticles', + headers: { token: localStorage.getItem('token') } + }) + .then(({ data }) => { + this.ownArticles = data.result; + }) + .catch((err) => { + console.log(err); + }); + }, + handleFileUpload: function () { + this.file = this.$refs.file.files[0]; + console.log(this.file); + }, + addArticle: function () { + + let data = new FormData(); + data.append('file', this.file); + data.append('title', this.title); + data.append('description', this.description); + data.append('content', this.text); + + axios({ + method: 'POST', + url: 'http://localhost:3000/articles', + data, + // data: { + // title: this.title, + // description: this.description, + // content: this.text, + // }, + headers: { + token: localStorage.getItem('token'), + 'Content-Type': 'multipart/form-data' + } + }) + .then(({ data: { result: data } }) => { + this.clearValue(); + this.myArticles(); + this.allArticles(); + // this.ownArticles.push(data.data); + // this.discoverArticles.push(data.data); + }) + .catch((err) => { + console.log(err); + }); + }, + formEdit: function (article) { + this.title = article.title; + this.description = article.description; + this.text = article.content; + this.id = article._id; + }, + editArticle: function () { + axios({ + method: 'PUT', + url: `http://localhost:3000/articles/${this.id}`, + data: { + title: this.title, + description: this.description, + content: this.text, + }, + headers: { token: localStorage.getItem('token') } + }) + .then((response) => { + // this.ownArticles.map((data, idx) => { + // if (data._id === response.data.result.data._id) { + // return this.ownArticles[idx] = response.data.result.data + // } + // }); + this.clearValue(); + this.myArticles(); + this.allArticles(); + }) + .catch((err) => { + console.log(err); + }); + }, + deleteArticle: function (id) { + axios({ + method: 'DELETE', + url: `http://localhost:3000/articles/${id}`, + headers: { token: localStorage.getItem('token') } + }) + .then((response) => { + // const idx = this.ownArticles.findIndex(e => { + // return e == id; + // }); + // this.ownArticles.splice(idx, 1); + this.allArticles(); + this.myArticles(); + }) + .catch((err) => { + console.log(err); + }); + }, + readArticle: function (article) { + this.readArt = article; + this.author = this.readArt.author.name + console.log(this.readArt.author); + } + }, + created: function () { + this.allArticles(); + }, + mounted: function () { + if (localStorage.getItem('token')) { + this.checkUser(); + this.myArticles(); + } + }, + computed: { + filterOwnArticle: function () { + return this.ownArticles.filter(art => { + let title = art.title; + if (title) return new RegExp('.*' + this.qOwnSearch + '.*').test(title.toLowerCase()); + }) + }, + filterAllArticle: function () { + return this.discoverArticles.filter(art => { + let title = art.title; + if (title) return new RegExp('.*' + this.qSearch + '.*').test(title.toLowerCase()); + }) + } + } +}); \ No newline at end of file diff --git a/client/js/vueWysiwyg.js b/client/js/vueWysiwyg.js new file mode 100644 index 0000000..296b091 --- /dev/null +++ b/client/js/vueWysiwyg.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("vueWysiwyg",[],t):"object"==typeof exports?exports.vueWysiwyg=t():e.vueWysiwyg=t()}("undefined"!=typeof self?self:this,function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:i})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/vue-wysiwyg/",n(n.s=18)}([function(e,t,n){"use strict";t.a=function(e,t,n,i,o,r,s,a){var l=typeof(e=e||{}).default;"object"!==l&&"function"!==l||(e=e.default);var u,c="function"==typeof e?e.options:e;t&&(c.render=t,c.staticRenderFns=n,c._compiled=!0);i&&(c.functional=!0);r&&(c._scopeId=r);s?(u=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),o&&o.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(s)},c._ssrRegister=u):o&&(u=a?function(){o.call(this,this.$root.$options.shadowRoot)}:o);if(u)if(c.functional){c._injectStyles=u;var d=c.render;c.render=function(e,t){return u.call(t),d(e,t)}}else{var p=c.beforeCreate;c.beforeCreate=p?[].concat(p,u):[u]}return{exports:e,options:c}}},function(e,t,n){"use strict";var i=new function(){var e={listeners:{},on:function(t,n){void 0===e.listeners[t]&&(e.listeners[t]=[]),e.listeners[t].push(n)},emit:function(t){for(var n=arguments.length,i=Array(10?i:n)(e)}},function(e,t,n){"use strict";var i=n(2),o=n.n(i),r=n(1),s=n(50),a=n.n(s),l=n(51),u=n(53),c=n.n(u),d=n(54),p=n.n(d),h=n(55),f=n.n(h),v=n(56),m=n.n(v),g=n(57),b=n.n(g),w=n(58),y=n.n(w),q=n(59),k=n(61),z=n(63),x=n.n(z),F=n(64),E=n.n(F),_=n(65),C=n.n(_),S=n(66),L=n(72),M=n(75),T=n.n(M),A=n(76),O=n.n(A),D=[c.a,p.a,f.a,O.a,m.a,b.a,y.a,O.a,q.a,k.a,x.a,E.a,C.a,O.a,S.a,L.a,O.a,T.a];t.a={model:{prop:"html",event:"html"},props:{html:{type:String,default:""},placeholder:{type:String,default:"Enter text..."},options:Object},components:{Btn:l.a},data:function(){return{selection:""}},computed:{mergedOptions:function(){return o()({},r.a.options,this.options)},modules:function(){var e=this,t=this.mergedOptions.iconOverrides;return D.filter(function(t){return void 0===e.mergedOptions.hideModules||!e.mergedOptions.hideModules[t.title]}).map(function(e){return void 0!==t&&void 0!==t[e.title]&&(e.icon=t[e.title]),e}).concat(this.mergedOptions.customModules)},btnsWithDashboards:function(){return this.modules?this.modules.filter(function(e){return e.render}):[]},innerHTML:{get:function(){return this.$refs.content.innerHTML},set:function(e){this.$refs.content.innerHTML!==e&&(this.$refs.content.innerHTML=e)}}},methods:{saveSelection:function(){if(void 0!==window.getSelection){if(this.selection=window.getSelection(),this.selection.getRangeAt&&this.selection.rangeCount)return this.selection.getRangeAt(0)}else if(document.selection&&document.selection.createRange)return document.selection.createRange();return null},restoreSelection:function(e){e&&(void 0===window.getSelection?document.selection&&e.select&&e.select():(this.selection=window.getSelection(),this.selection.removeAllRanges(),this.selection.addRange(e)))},clearSelection:function(){this.selection=null;var e=window.getSelection();e&&(void 0!==e.empty&&e.empty(),void 0!==e.removeAllRanges&&e.removeAllRanges())},exec:function(e,t,n){!1!==n&&this.selection&&this.restoreSelection(this.selection),document.execCommand(e,!1,t||""),this.clearSelection(),this.$nextTick(this.emit)},onDocumentClick:function(e){for(var t,n=0;n',methods:{insertHeading:function(e){this.$parent.closeDashboard(),this.$emit("exec","formatBlock",e.target.textContent)}}}},function(e,t,n){"use strict";var i=n(1);t.a={title:"link",icon:'',description:"Hyperlink",props:{uid:null},data:function(){return{url:"",title:""}},methods:{insertLink:function(){this.$emit("exec","insertHTML",""+this.title+""),this.$parent.closeDashboard(),this.url="",this.title=""}},created:function(){var e=this;i.a.on(this.uid+"_show_dashboard_link",function(){e.$nextTick(function(){e.$refs.url.focus()})})}}},function(e,t,n){"use strict";var i=n(2),o=n.n(i),r=n(67),s=n.n(r),a=(n(1),n(70));n.n(a);t.a={title:"image",icon:'',description:"Insert Image",props:["options"],components:{Dropzone:s.a},computed:{uploadURL:function(){return this.options.image.uploadURL},dropzoneOptions:function(){return o()({},this.options.image.dropzoneOptions,{id:this._uid+"vwdropzone",url:this.uploadURL,autoProcessQueue:"None"!==this.uploadURL,dictDefaultMessage:'
Click here to upload...'})}},methods:{fileUploaded:function(e,t){t&&this.$emit("exec","insertHTML","")},fileAdded:function(e){var t=this;if(!e||"None"===this.uploadURL){var n=new FileReader;n.addEventListener("load",function(){t.$emit("exec","insertHTML","")},!1),n.readAsDataURL(e)}}}}},function(e,t,n){"use strict";t.a={title:"table",description:"Insert Table",icon:'',data:function(){return{rows:2,cols:2}},methods:{insertTable:function(){var e=(""+"".repeat(this.cols)+"").repeat(this.rows);this.$emit("exec","insertHTML",""+e+"
"),this.$parent.closeDashboard()}}}},function(e,t,n){e.exports=n(19)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(2),o=n.n(i),r=n(48),s=n(1);t.default={install:function(e){var t=1u;)for(var p,h=a(arguments[u++]),f=c?i(h).concat(c(h)):i(h),v=f.length,m=0;v>m;)d.call(h,p=f[m++])&&(n[p]=h[p]);return n}:l},function(e,t,n){var i=n(35),o=n(44);e.exports=Object.keys||function(e){return i(e,o)}},function(e,t,n){var i=n(36),o=n(8),r=n(38)(!1),s=n(41)("IE_PROTO");e.exports=function(e,t){var n,a=o(e),l=0,u=[];for(n in a)n!=s&&i(a,n)&&u.push(n);for(;t.length>l;)i(a,n=t[l++])&&(~r(u,n)||u.push(n));return u}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){var i=n(8),o=n(39),r=n(40);e.exports=function(e){return function(t,n,s){var a,l=i(t),u=o(l.length),c=r(s,u);if(e&&n!=n){for(;u>c;)if((a=l[c++])!=a)return!0}else for(;u>c;c++)if((e||c in l)&&l[c]===n)return e||c||0;return!e&&-1}}},function(e,t,n){var i=n(11),o=Math.min;e.exports=function(e){return e>0?o(i(e),9007199254740991):0}},function(e,t,n){var i=n(11),o=Math.max,r=Math.min;e.exports=function(e,t){return(e=i(e))<0?o(e+t,0):r(e,t)}},function(e,t,n){var i=n(42)("keys"),o=n(43);e.exports=function(e){return i[e]||(i[e]=o(e))}},function(e,t,n){var i=n(3),o=i["__core-js_shared__"]||(i["__core-js_shared__"]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t){var n=0,i=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+i).toString(36))}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){var i=n(10);e.exports=function(e){return Object(i(e))}},function(e,t,n){"use strict";var i=n(12),o=n(77),r=n(0);var s=function(e){n(49)},a=Object(r.a)(i.a,o.a,o.b,!1,s,null,null);t.a=a.exports},function(e,t){},function(e,t){e.exports=function(e,t,n){var i,o,r,s,a;function l(){var u=Date.now()-s;u=0?i=setTimeout(l,t-u):(i=null,n||(a=e.apply(r,o),r=o=null))}null==t&&(t=100);var u=function(){r=this,o=arguments,s=Date.now();var u=n&&!i;return i||(i=setTimeout(l,t)),u&&(a=e.apply(r,o),r=o=null),a};return u.clear=function(){i&&(clearTimeout(i),i=null)},u.flush=function(){i&&(a=e.apply(r,o),r=o=null,clearTimeout(i),i=null)},u}},function(e,t,n){"use strict";var i=n(13),o=n(52),r=n(0),s=Object(r.a)(i.a,o.a,o.b,!1,null,null,null);t.a=s.exports},function(e,t,n){"use strict";n.d(t,"a",function(){return i}),n.d(t,"b",function(){return o});var i=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{on:{mousedown:e.onBtnClick}},[n("a",{class:"vw-btn-"+e.module.title,domProps:{innerHTML:e._s(e.module.icon)}}),n("div",{directives:[{name:"show",rawName:"v-show",value:e.showDashboard,expression:"showDashboard"}],ref:"dashboard",staticClass:"dashboard"},[e.module.render?e._m(0):e._e()],1)])},o=[function(){var e=this.$createElement;return(this._self._c||e)(this.module,{ref:"moduleDashboard",tag:"component",attrs:{uid:this.uid,options:this.options},on:{exec:this.exec}})}]},function(e,t){e.exports={title:"bold",action:["bold"],description:"Bold",icon:''}},function(e,t){e.exports={title:"italic",description:"Italic",action:["italic"],icon:''}},function(e,t){e.exports={title:"underline",action:["underline"],description:"Underline",icon:''}},function(e,t){e.exports={title:"justifyLeft",action:["justifyLeft"],description:"Justify Left",icon:''}},function(e,t){e.exports={title:"justifyCenter",action:["justifyCenter"],description:"Center",icon:''}},function(e,t){e.exports={title:"justifyRight",action:["justifyRight"],description:"Justify Right",icon:''}},function(e,t,n){"use strict";var i=n(14),o=n(60),r=n(0),s=Object(r.a)(i.a,o.a,o.b,!1,null,null,null);t.a=s.exports},function(e,t,n){"use strict";n.d(t,"a",function(){return i}),n.d(t,"b",function(){return o});var i=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",[n("button",{attrs:{type:"button"},on:{click:e.insertHeading}},[e._v("H1")]),e._v(" "),n("button",{attrs:{type:"button"},on:{click:e.insertHeading}},[e._v("H2")]),e._v(" "),n("button",{attrs:{type:"button"},on:{click:e.insertHeading}},[e._v("H3")]),e._v(" "),n("button",{attrs:{type:"button"},on:{click:e.insertHeading}},[e._v("H4")]),e._v(" "),n("button",{attrs:{type:"button"},on:{click:e.insertHeading}},[e._v("H5")]),e._v(" "),n("button",{attrs:{type:"button"},on:{click:e.insertHeading}},[e._v("H6")])])},o=[]},function(e,t,n){"use strict";var i=n(15),o=n(62),r=n(0),s=Object(r.a)(i.a,o.a,o.b,!1,null,null,null);t.a=s.exports},function(e,t,n){"use strict";n.d(t,"a",function(){return i}),n.d(t,"b",function(){return o});var i=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("form",{on:{submit:function(t){t.preventDefault(),e.insertLink(t)}}},[n("label",[e._v("\n URL\n "),n("input",{directives:[{name:"model",rawName:"v-model",value:e.url,expression:"url"}],ref:"url",staticStyle:{width:"40%"},attrs:{type:"text"},domProps:{value:e.url},on:{input:function(t){t.target.composing||(e.url=t.target.value)}}})]),e._v(" "),n("label",[e._v("\n Link Title\n "),n("input",{directives:[{name:"model",rawName:"v-model",value:e.title,expression:"title"}],staticStyle:{width:"40%"},attrs:{type:"text"},domProps:{value:e.title},on:{input:function(t){t.target.composing||(e.title=t.target.value)}}})]),e._v(" "),n("button",{attrs:{type:"submit"}},[e._v("Insert")])])},o=[]},function(e,t){e.exports={title:"code",icon:'',description:"Code",action:["formatBlock","pre"]}},function(e,t){e.exports={title:"orderedList",action:["insertOrderedList"],description:"Ordered List (1, 2, 3)",icon:''}},function(e,t){e.exports={title:"unorderedList",action:["insertUnorderedList"],description:"Bullet List",icon:''}},function(e,t,n){"use strict";var i=n(16),o=n(71),r=n(0),s=Object(r.a)(i.a,o.a,o.b,!1,null,null,null);t.a=s.exports},function(e,t,n){var i;i=function(){"use strict";var e={getSignedURL:function(e,t){var n={filePath:e.name,contentType:e.type};return new Promise(function(i,o){var r=new FormData,s=new XMLHttpRequest,a="function"==typeof t.signingURL?t.signingURL(e):t.signingURL;s.open("POST",a),s.onload=function(){200==s.status?i(JSON.parse(s.response)):o(s.statusText)},s.onerror=function(e){console.error("Network Error : Could not send request to AWS (Maybe CORS errors)"),o(e)},Object.entries(t.headers||{}).forEach(function(e){var t=e[0],n=e[1];s.setRequestHeader(t,n)}),n=Object.assign(n,t.params||{}),Object.entries(n).forEach(function(e){var t=e[0],n=e[1];r.append(t,n)}),s.send(r)})},sendFile:function(e,t){var n=new FormData;return this.getSignedURL(e,t).then(function(t){var i=t.signature;return Object.keys(i).forEach(function(e){n.append(e,i[e])}),n.append("file",e),new Promise(function(e,i){var o=new XMLHttpRequest;o.open("POST",t.postEndpoint),o.onload=function(){if(201==o.status){var t=(new window.DOMParser).parseFromString(o.response,"text/xml").firstChild.children[0].innerHTML;e({success:!0,message:t})}else{var n=(new window.DOMParser).parseFromString(o.response,"text/xml").firstChild.children[0].innerHTML;i({success:!1,message:n+". Request is marked as resolved when returns as status 201"})}},o.onerror=function(e){var t=(new window.DOMParser).parseFromString(o.response,"text/xml").firstChild.children[1].innerHTML;i({success:!1,message:t})},o.send(n)})}).catch(function(e){return e})}};return{render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("div",{ref:"dropzoneElement",class:{"vue-dropzone dropzone":e.includeStyling},attrs:{id:e.id}})},staticRenderFns:[],props:{id:{type:String,required:!0},options:{type:Object,required:!0},includeStyling:{type:Boolean,default:!0,required:!1},awss3:{type:Object,required:!1,default:null},destroyDropzone:{type:Boolean,default:!0,required:!1}},data:function(){return{isS3:!1,wasQueueAutoProcess:!0}},computed:{dropzoneSettings:function(){var e={thumbnailWidth:200,thumbnailHeight:200};return Object.keys(this.options).forEach(function(t){e[t]=this.options[t]},this),null!==this.awss3&&(e.autoProcessQueue=!1,this.isS3=!0,void 0!==this.options.autoProcessQueue&&(this.wasQueueAutoProcess=this.options.autoProcessQueue)),e}},methods:{manuallyAddFile:function(e,t){e.manuallyAdded=!0,this.dropzone.emit("addedfile",e),t&&this.dropzone.emit("thumbnail",e,t);for(var n=e.previewElement.querySelectorAll("[data-dz-thumbnail]"),i=0;i1?n-1:0),o=1;o=s.length)break;s[r++].apply(this,i)}}return this}},{key:"off",value:function(e,t){if(!this._callbacks||0===arguments.length)return this._callbacks={},this;var n=this._callbacks[e];if(!n)return this;if(1===arguments.length)return delete this._callbacks[e],this;for(var i=0;i=n.length)break;var i=n[t++];if(/(^| )dz-message($| )/.test(i.className)){e=i,i.className="dz-message";break}}e||(e=r.createElement('
'),this.element.appendChild(e));var o=e.getElementsByTagName("span")[0];return o&&(null!=o.textContent?o.textContent=this.options.dictFallbackMessage:null!=o.innerText&&(o.innerText=this.options.dictFallbackMessage)),this.element.appendChild(this.getFallbackForm())},resize:function(e,t,n,i){var o={srcX:0,srcY:0,srcWidth:e.width,srcHeight:e.height},r=e.width/e.height;null==t&&null==n?(t=o.srcWidth,n=o.srcHeight):null==t?t=n*r:null==n&&(n=t/r);var s=(t=Math.min(t,o.srcWidth))/(n=Math.min(n,o.srcHeight));if(o.srcWidth>t||o.srcHeight>n)if("crop"===i)r>s?(o.srcHeight=e.height,o.srcWidth=o.srcHeight*s):(o.srcWidth=e.width,o.srcHeight=o.srcWidth/s);else{if("contain"!==i)throw new Error("Unknown resizeMethod '"+i+"'");r>s?n=t/r:t=n*r}return o.srcX=(e.width-o.srcWidth)/2,o.srcY=(e.height-o.srcHeight)/2,o.trgWidth=t,o.trgHeight=n,o},transformFile:function(e,t){return(this.options.resizeWidth||this.options.resizeHeight)&&e.type.match(/image.*/)?this.resizeImage(e,this.options.resizeWidth,this.options.resizeHeight,this.options.resizeMethod,t):t(e)},previewTemplate:'
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
',drop:function(e){return this.element.classList.remove("dz-drag-hover")},dragstart:function(e){},dragend:function(e){return this.element.classList.remove("dz-drag-hover")},dragenter:function(e){return this.element.classList.add("dz-drag-hover")},dragover:function(e){return this.element.classList.add("dz-drag-hover")},dragleave:function(e){return this.element.classList.remove("dz-drag-hover")},paste:function(e){},reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(e){var t=this;if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer){e.previewElement=r.createElement(this.options.previewTemplate.trim()),e.previewTemplate=e.previewElement,this.previewsContainer.appendChild(e.previewElement);for(var n=0,i=i=e.previewElement.querySelectorAll("[data-dz-name]");;){if(n>=i.length)break;var o=i[n++];o.textContent=e.name}for(var s=0,a=a=e.previewElement.querySelectorAll("[data-dz-size]");!(s>=a.length);)(o=a[s++]).innerHTML=this.filesize(e.size);this.options.addRemoveLinks&&(e._removeLink=r.createElement(''+this.options.dictRemoveFile+""),e.previewElement.appendChild(e._removeLink));for(var l=function(n){return n.preventDefault(),n.stopPropagation(),e.status===r.UPLOADING?r.confirm(t.options.dictCancelUploadConfirmation,function(){return t.removeFile(e)}):t.options.dictRemoveFileConfirmation?r.confirm(t.options.dictRemoveFileConfirmation,function(){return t.removeFile(e)}):t.removeFile(e)},u=0,c=c=e.previewElement.querySelectorAll("[data-dz-remove]");;){if(u>=c.length)break;c[u++].addEventListener("click",l)}}},removedfile:function(e){return null!=e.previewElement&&null!=e.previewElement.parentNode&&e.previewElement.parentNode.removeChild(e.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(e,t){if(e.previewElement){e.previewElement.classList.remove("dz-file-preview");for(var n=0,i=i=e.previewElement.querySelectorAll("[data-dz-thumbnail]");;){if(n>=i.length)break;var o=i[n++];o.alt=e.name,o.src=t}return setTimeout(function(){return e.previewElement.classList.add("dz-image-preview")},1)}},error:function(e,t){if(e.previewElement){e.previewElement.classList.add("dz-error"),"String"!=typeof t&&t.error&&(t=t.error);for(var n=0,i=i=e.previewElement.querySelectorAll("[data-dz-errormessage]");;){if(n>=i.length)break;i[n++].textContent=t}}},errormultiple:function(){},processing:function(e){if(e.previewElement&&(e.previewElement.classList.add("dz-processing"),e._removeLink))return e._removeLink.textContent=this.options.dictCancelUpload},processingmultiple:function(){},uploadprogress:function(e,t,n){if(e.previewElement)for(var i=0,o=o=e.previewElement.querySelectorAll("[data-dz-uploadprogress]");;){if(i>=o.length)break;var r=o[i++];"PROGRESS"===r.nodeName?r.value=t:r.style.width=t+"%"}},totaluploadprogress:function(){},sending:function(){},sendingmultiple:function(){},success:function(e){if(e.previewElement)return e.previewElement.classList.add("dz-success")},successmultiple:function(){},canceled:function(e){return this.emit("error",e,"Upload canceled.")},canceledmultiple:function(){},complete:function(e){if(e._removeLink&&(e._removeLink.textContent=this.options.dictRemoveFile),e.previewElement)return e.previewElement.classList.add("dz-complete")},completemultiple:function(){},maxfilesexceeded:function(){},maxfilesreached:function(){},queuecomplete:function(){},addedfiles:function(){}},this.prototype._thumbnailQueue=[],this.prototype._processingThumbnail=!1}},{key:"extend",value:function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),i=1;i=r.length)break;var s=r[o++];for(var a in s){var l=s[a];e[a]=l}}return e}}]),t(r,[{key:"getAcceptedFiles",value:function(){return this.files.filter(function(e){return e.accepted}).map(function(e){return e})}},{key:"getRejectedFiles",value:function(){return this.files.filter(function(e){return!e.accepted}).map(function(e){return e})}},{key:"getFilesWithStatus",value:function(e){return this.files.filter(function(t){return t.status===e}).map(function(e){return e})}},{key:"getQueuedFiles",value:function(){return this.getFilesWithStatus(r.QUEUED)}},{key:"getUploadingFiles",value:function(){return this.getFilesWithStatus(r.UPLOADING)}},{key:"getAddedFiles",value:function(){return this.getFilesWithStatus(r.ADDED)}},{key:"getActiveFiles",value:function(){return this.files.filter(function(e){return e.status===r.UPLOADING||e.status===r.QUEUED}).map(function(e){return e})}},{key:"init",value:function(){var e=this;if("form"===this.element.tagName&&this.element.setAttribute("enctype","multipart/form-data"),this.element.classList.contains("dropzone")&&!this.element.querySelector(".dz-message")&&this.element.appendChild(r.createElement('
'+this.options.dictDefaultMessage+"
")),this.clickableElements.length){!function t(){return e.hiddenFileInput&&e.hiddenFileInput.parentNode.removeChild(e.hiddenFileInput),e.hiddenFileInput=document.createElement("input"),e.hiddenFileInput.setAttribute("type","file"),(null===e.options.maxFiles||e.options.maxFiles>1)&&e.hiddenFileInput.setAttribute("multiple","multiple"),e.hiddenFileInput.className="dz-hidden-input",null!==e.options.acceptedFiles&&e.hiddenFileInput.setAttribute("accept",e.options.acceptedFiles),null!==e.options.capture&&e.hiddenFileInput.setAttribute("capture",e.options.capture),e.hiddenFileInput.style.visibility="hidden",e.hiddenFileInput.style.position="absolute",e.hiddenFileInput.style.top="0",e.hiddenFileInput.style.left="0",e.hiddenFileInput.style.height="0",e.hiddenFileInput.style.width="0",document.querySelector(e.options.hiddenInputContainer).appendChild(e.hiddenFileInput),e.hiddenFileInput.addEventListener("change",function(){var n=e.hiddenFileInput.files;if(n.length)for(var i=0,o=o=n;!(i>=o.length);){var r=o[i++];e.addFile(r)}return e.emit("addedfiles",n),t()})}()}this.URL=null!==window.URL?window.URL:window.webkitURL;for(var t=0,n=n=this.events;;){if(t>=n.length)break;var i=n[t++];this.on(i,this.options[i])}this.on("uploadprogress",function(){return e.updateTotalUploadProgress()}),this.on("removedfile",function(){return e.updateTotalUploadProgress()}),this.on("canceled",function(t){return e.emit("complete",t)}),this.on("complete",function(t){if(0===e.getAddedFiles().length&&0===e.getUploadingFiles().length&&0===e.getQueuedFiles().length)return setTimeout(function(){return e.emit("queuecomplete")},0)});var o=function(e){return e.stopPropagation(),e.preventDefault?e.preventDefault():e.returnValue=!1};return this.listeners=[{element:this.element,events:{dragstart:function(t){return e.emit("dragstart",t)},dragenter:function(t){return o(t),e.emit("dragenter",t)},dragover:function(t){var n=void 0;try{n=t.dataTransfer.effectAllowed}catch(e){}return t.dataTransfer.dropEffect="move"===n||"linkMove"===n?"move":"copy",o(t),e.emit("dragover",t)},dragleave:function(t){return e.emit("dragleave",t)},drop:function(t){return o(t),e.drop(t)},dragend:function(t){return e.emit("dragend",t)}}}],this.clickableElements.forEach(function(t){return e.listeners.push({element:t,events:{click:function(n){return(t!==e.element||n.target===e.element||r.elementInside(n.target,e.element.querySelector(".dz-message")))&&e.hiddenFileInput.click(),!0}}})}),this.enable(),this.options.init.call(this)}},{key:"destroy",value:function(){return this.disable(),this.removeAllFiles(!0),(null!=this.hiddenFileInput?this.hiddenFileInput.parentNode:void 0)&&(this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput),this.hiddenFileInput=null),delete this.element.dropzone,r.instances.splice(r.instances.indexOf(this),1)}},{key:"updateTotalUploadProgress",value:function(){var e=void 0,t=0,n=0;if(this.getActiveFiles().length){for(var i=0,o=o=this.getActiveFiles();;){if(i>=o.length)break;var r=o[i++];t+=r.upload.bytesSent,n+=r.upload.total}e=100*t/n}else e=100;return this.emit("totaluploadprogress",e,n,t)}},{key:"_getParamName",value:function(e){return"function"==typeof this.options.paramName?this.options.paramName(e):this.options.paramName+(this.options.uploadMultiple?"["+e+"]":"")}},{key:"_renameFile",value:function(e){return"function"!=typeof this.options.renameFile?e.name:this.options.renameFile(e)}},{key:"getFallbackForm",value:function(){var e,t=void 0;if(e=this.getExistingFallback())return e;var n='
';this.options.dictFallbackText&&(n+="

"+this.options.dictFallbackText+"

"),n+='
';var i=r.createElement(n);return"FORM"!==this.element.tagName?(t=r.createElement('
')).appendChild(i):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=t?t:i}},{key:"getExistingFallback",value:function(){for(var e=function(e){for(var t=0,n=n=e;;){if(t>=n.length)break;var i=n[t++];if(/(^| )fallback($| )/.test(i.className))return i}},t=["div","form"],n=0;n0){for(var i=["tb","gb","mb","kb","b"],o=0;o=Math.pow(this.options.filesizeBase,4-o)/10){t=e/Math.pow(this.options.filesizeBase,4-o),n=r;break}}t=Math.round(10*t)/10}return""+t+" "+this.options.dictFileSizeUnits[n]}},{key:"_updateMaxFilesReachedClass",value:function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")}},{key:"drop",value:function(e){if(e.dataTransfer){this.emit("drop",e);var t=e.dataTransfer.files;if(this.emit("addedfiles",t),t.length){var n=e.dataTransfer.items;n&&n.length&&null!=n[0].webkitGetAsEntry?this._addFilesFromItems(n):this.handleFiles(t)}}}},{key:"paste",value:function(e){if(null!=(void 0!==(t=null!=e?e.clipboardData:void 0)&&null!==t?function(e){return e.items}(t):void 0)){var t;this.emit("paste",e);var n=e.clipboardData.items;return n.length?this._addFilesFromItems(n):void 0}}},{key:"handleFiles",value:function(e){var t=this;return e.map(function(e){return t.addFile(e)})}},{key:"_addFilesFromItems",value:function(e){var t=this;return function(){for(var n=[],i=0,o=o=e;;){if(i>=o.length)break;var r,s=o[i++];null!=s.webkitGetAsEntry&&(r=s.webkitGetAsEntry())?r.isFile?n.push(t.addFile(s.getAsFile())):r.isDirectory?n.push(t._addFilesFromDirectory(r,r.name)):n.push(void 0):null!=s.getAsFile&&(null==s.kind||"file"===s.kind)?n.push(t.addFile(s.getAsFile())):n.push(void 0)}return n}()}},{key:"_addFilesFromDirectory",value:function(e,t){var n=this,i=e.createReader(),o=function(e){return t=console,n="log",i=function(t){return t.log(e)},void 0!==t&&null!==t&&"function"==typeof t[n]?i(t,n):void 0;var t,n,i};return function e(){return i.readEntries(function(i){if(i.length>0){for(var o=0,r=r=i;!(o>=r.length);){var s=r[o++];s.isFile?s.file(function(e){if(!n.options.ignoreHiddenFiles||"."!==e.name.substring(0,1))return e.fullPath=t+"/"+e.name,n.addFile(e)}):s.isDirectory&&n._addFilesFromDirectory(s,t+"/"+s.name)}e()}return null},o)}()}},{key:"accept",value:function(e,t){return e.size>1024*this.options.maxFilesize*1024?t(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(e.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):r.isValidFile(e,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(t(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",e)):this.options.accept.call(this,e,t):t(this.options.dictInvalidFileType)}},{key:"addFile",value:function(e){var t=this;return e.upload={uuid:r.uuidv4(),progress:0,total:e.size,bytesSent:0,filename:this._renameFile(e),chunked:this.options.chunking&&(this.options.forceChunking||e.size>this.options.chunkSize),totalChunkCount:Math.ceil(e.size/this.options.chunkSize)},this.files.push(e),e.status=r.ADDED,this.emit("addedfile",e),this._enqueueThumbnail(e),this.accept(e,function(n){return n?(e.accepted=!1,t._errorProcessing([e],n)):(e.accepted=!0,t.options.autoQueue&&t.enqueueFile(e)),t._updateMaxFilesReachedClass()})}},{key:"enqueueFiles",value:function(e){for(var t=0,n=n=e;;){if(t>=n.length)break;var i=n[t++];this.enqueueFile(i)}return null}},{key:"enqueueFile",value:function(e){var t=this;if(e.status!==r.ADDED||!0!==e.accepted)throw new Error("This file can't be queued because it has already been processed or was rejected.");if(e.status=r.QUEUED,this.options.autoProcessQueue)return setTimeout(function(){return t.processQueue()},0)}},{key:"_enqueueThumbnail",value:function(e){var t=this;if(this.options.createImageThumbnails&&e.type.match(/image.*/)&&e.size<=1024*this.options.maxThumbnailFilesize*1024)return this._thumbnailQueue.push(e),setTimeout(function(){return t._processThumbnailQueue()},0)}},{key:"_processThumbnailQueue",value:function(){var e=this;if(!this._processingThumbnail&&0!==this._thumbnailQueue.length){this._processingThumbnail=!0;var t=this._thumbnailQueue.shift();return this.createThumbnail(t,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,!0,function(n){return e.emit("thumbnail",t,n),e._processingThumbnail=!1,e._processThumbnailQueue()})}}},{key:"removeFile",value:function(e){if(e.status===r.UPLOADING&&this.cancelUpload(e),this.files=s(this.files,e),this.emit("removedfile",e),0===this.files.length)return this.emit("reset")}},{key:"removeAllFiles",value:function(e){null==e&&(e=!1);for(var t=0,n=n=this.files.slice();;){if(t>=n.length)break;var i=n[t++];(i.status!==r.UPLOADING||e)&&this.removeFile(i)}return null}},{key:"resizeImage",value:function(e,t,n,i,o){var s=this;return this.createThumbnail(e,t,n,i,!1,function(t,n){if(null===n)return o(e);var i=s.options.resizeMimeType;null==i&&(i=e.type);var a=n.toDataURL(i,s.options.resizeQuality);return"image/jpeg"!==i&&"image/jpg"!==i||(a=u.restore(e.dataURL,a)),o(r.dataURItoBlob(a))})}},{key:"createThumbnail",value:function(e,t,n,i,o,r){var s=this,a=new FileReader;return a.onload=function(){if(e.dataURL=a.result,"image/svg+xml"!==e.type)return s.createThumbnailFromUrl(e,t,n,i,o,r);null!=r&&r(a.result)},a.readAsDataURL(e)}},{key:"createThumbnailFromUrl",value:function(e,t,n,i,o,r,s){var a=this,u=document.createElement("img");return s&&(u.crossOrigin=s),u.onload=function(){var s=function(e){return e(1)};return"undefined"!=typeof EXIF&&null!==EXIF&&o&&(s=function(e){return EXIF.getData(u,function(){return e(EXIF.getTag(this,"Orientation"))})}),s(function(o){e.width=u.width,e.height=u.height;var s=a.options.resize.call(a,e,t,n,i),c=document.createElement("canvas"),d=c.getContext("2d");switch(c.width=s.trgWidth,c.height=s.trgHeight,o>4&&(c.width=s.trgHeight,c.height=s.trgWidth),o){case 2:d.translate(c.width,0),d.scale(-1,1);break;case 3:d.translate(c.width,c.height),d.rotate(Math.PI);break;case 4:d.translate(0,c.height),d.scale(1,-1);break;case 5:d.rotate(.5*Math.PI),d.scale(1,-1);break;case 6:d.rotate(.5*Math.PI),d.translate(0,-c.height);break;case 7:d.rotate(.5*Math.PI),d.translate(c.width,-c.height),d.scale(-1,1);break;case 8:d.rotate(-.5*Math.PI),d.translate(-c.width,0)}l(d,u,null!=s.srcX?s.srcX:0,null!=s.srcY?s.srcY:0,s.srcWidth,s.srcHeight,null!=s.trgX?s.trgX:0,null!=s.trgY?s.trgY:0,s.trgWidth,s.trgHeight);var p=c.toDataURL("image/png");if(null!=r)return r(p,c)})},null!=r&&(u.onerror=r),u.src=e.dataURL}},{key:"processQueue",value:function(){var e=this.options.parallelUploads,t=this.getUploadingFiles().length,n=t;if(!(t>=e)){var i=this.getQueuedFiles();if(i.length>0){if(this.options.uploadMultiple)return this.processFiles(i.slice(0,e-t));for(;n=n.length)break;var i=n[t++];i.processing=!0,i.status=r.UPLOADING,this.emit("processing",i)}return this.options.uploadMultiple&&this.emit("processingmultiple",e),this.uploadFiles(e)}},{key:"_getFilesWithXhr",value:function(e){return this.files.filter(function(t){return t.xhr===e}).map(function(e){return e})}},{key:"cancelUpload",value:function(e){if(e.status===r.UPLOADING){for(var t=this._getFilesWithXhr(e.xhr),n=0,i=i=t;;){if(n>=i.length)break;i[n++].status=r.CANCELED}void 0!==e.xhr&&e.xhr.abort();for(var o=0,s=s=t;;){if(o>=s.length)break;var a=s[o++];this.emit("canceled",a)}this.options.uploadMultiple&&this.emit("canceledmultiple",t)}else e.status!==r.ADDED&&e.status!==r.QUEUED||(e.status=r.CANCELED,this.emit("canceled",e),this.options.uploadMultiple&&this.emit("canceledmultiple",[e]));if(this.options.autoProcessQueue)return this.processQueue()}},{key:"resolveOption",value:function(e){if("function"==typeof e){for(var t=arguments.length,n=Array(t>1?t-1:0),i=1;i=i.upload.totalChunkCount)){0;var s=n*t.options.chunkSize,a=Math.min(s+t.options.chunkSize,i.size),l={name:t._getParamName(0),data:o.webkitSlice?o.webkitSlice(s,a):o.slice(s,a),filename:i.upload.filename,chunkIndex:n};i.upload.chunks[n]={file:i,index:n,dataBlock:l,status:r.UPLOADING,progress:0,retries:0},t._uploadData(e,[l])}};if(i.upload.finishedChunkUpload=function(n){var o=!0;n.status=r.SUCCESS,n.dataBlock=null;for(var a=0;a=s.length)break;s[o++].xhr=i}e[0].upload.chunked&&(e[0].upload.chunks[t[0].chunkIndex].xhr=i);var a=this.resolveOption(this.options.method,e),l=this.resolveOption(this.options.url,e);i.open(a,l,!0),i.timeout=this.resolveOption(this.options.timeout,e),i.withCredentials=!!this.options.withCredentials,i.onload=function(t){n._finishedUploading(e,i,t)},i.onerror=function(){n._handleUploadError(e,i)},(null!=i.upload?i.upload:i).onprogress=function(t){return n._updateFilesUploadProgress(e,i,t)};var u={Accept:"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};for(var c in this.options.headers&&r.extend(u,this.options.headers),u){var d=u[c];d&&i.setRequestHeader(c,d)}var p=new FormData;if(this.options.params){var h=this.options.params;for(var f in"function"==typeof h&&(h=h.call(this,e,i,e[0].upload.chunked?this._getChunk(e[0],i):null)),h){var v=h[f];p.append(f,v)}}for(var m=0,g=g=e;;){if(m>=g.length)break;var b=g[m++];this.emit("sending",b,i,p)}this.options.uploadMultiple&&this.emit("sendingmultiple",e,i,p),this._addFormElementData(p);for(var w=0;w=n.length)break;var i=n[t++],o=i.getAttribute("name"),r=i.getAttribute("type");if(r&&(r=r.toLowerCase()),void 0!==o&&null!==o)if("SELECT"===i.tagName&&i.hasAttribute("multiple"))for(var s=0,a=a=i.options;;){if(s>=a.length)break;var l=a[s++];l.selected&&e.append(o,l.value)}else(!r||"checkbox"!==r&&"radio"!==r||i.checked)&&e.append(o,i.value)}}},{key:"_updateFilesUploadProgress",value:function(e,t,n){var i=void 0;if(void 0!==n){if(i=100*n.loaded/n.total,e[0].upload.chunked){var o=e[0],r=this._getChunk(o,t);r.progress=i,r.total=n.total,r.bytesSent=n.loaded;o.upload.progress=0,o.upload.total=0,o.upload.bytesSent=0;for(var s=0;s=l.length)break;var u=l[a++];u.upload.progress=i,u.upload.total=n.total,u.upload.bytesSent=n.loaded}for(var c=0,d=d=e;;){if(c>=d.length)break;var p=d[c++];this.emit("uploadprogress",p,p.upload.progress,p.upload.bytesSent)}}else{var h=!0;i=100;for(var f=0,v=v=e;;){if(f>=v.length)break;var m=v[f++];100===m.upload.progress&&m.upload.bytesSent===m.upload.total||(h=!1),m.upload.progress=i,m.upload.bytesSent=m.upload.total}if(h)return;for(var g=0,b=b=e;;){if(g>=b.length)break;var w=b[g++];this.emit("uploadprogress",w,i,w.upload.bytesSent)}}}},{key:"_finishedUploading",value:function(e,t,n){var i=void 0;if(e[0].status!==r.CANCELED&&4===t.readyState){if("arraybuffer"!==t.responseType&&"blob"!==t.responseType&&(i=t.responseText,t.getResponseHeader("content-type")&&~t.getResponseHeader("content-type").indexOf("application/json")))try{i=JSON.parse(i)}catch(e){n=e,i="Invalid JSON response from server."}this._updateFilesUploadProgress(e),200<=t.status&&t.status<300?e[0].upload.chunked?e[0].upload.finishedChunkUpload(this._getChunk(e[0],t)):this._finished(e,i,n):this._handleUploadError(e,t,i)}}},{key:"_handleUploadError",value:function(e,t,n){if(e[0].status!==r.CANCELED){if(e[0].upload.chunked&&this.options.retryChunks){var i=this._getChunk(e[0],t);if(i.retries++=s.length)break;s[o++];this._errorProcessing(e,n||this.options.dictResponseError.replace("{{statusCode}}",t.status),t)}}}},{key:"submitRequest",value:function(e,t,n){e.send(t)}},{key:"_finished",value:function(e,t,n){for(var i=0,o=o=e;;){if(i>=o.length)break;var s=o[i++];s.status=r.SUCCESS,this.emit("success",s,t,n),this.emit("complete",s)}if(this.options.uploadMultiple&&(this.emit("successmultiple",e,t,n),this.emit("completemultiple",e)),this.options.autoProcessQueue)return this.processQueue()}},{key:"_errorProcessing",value:function(e,t,n){for(var i=0,o=o=e;;){if(i>=o.length)break;var s=o[i++];s.status=r.ERROR,this.emit("error",s,t,n),this.emit("complete",s)}if(this.options.uploadMultiple&&(this.emit("errormultiple",e,t,n),this.emit("completemultiple",e)),this.options.autoProcessQueue)return this.processQueue()}}],[{key:"uuidv4",value:function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}}]),r}();r.initClass(),r.version="5.3.0",r.options={},r.optionsForElement=function(e){return e.getAttribute("id")?r.options[a(e.getAttribute("id"))]:void 0},r.instances=[],r.forElement=function(e){if("string"==typeof e&&(e=document.querySelector(e)),null==(null!=e?e.dropzone:void 0))throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");return e.dropzone},r.autoDiscover=!0,r.discover=function(){var e=void 0;if(document.querySelectorAll)e=document.querySelectorAll(".dropzone");else{e=[];var t=function(t){return function(){for(var n=[],i=0,o=o=t;;){if(i>=o.length)break;var r=o[i++];/(^| )dropzone($| )/.test(r.className)?n.push(e.push(r)):n.push(void 0)}return n}()};t(document.getElementsByTagName("div")),t(document.getElementsByTagName("form"))}return function(){for(var t=[],n=0,i=i=e;;){if(n>=i.length)break;var o=i[n++];!1!==r.optionsForElement(o)?t.push(new r(o)):t.push(void 0)}return t}()},r.blacklistedBrowsers=[/opera.*(Macintosh|Windows Phone).*version\/12/i],r.isBrowserSupported=function(){var e=!0;if(window.File&&window.FileReader&&window.FileList&&window.Blob&&window.FormData&&document.querySelector)if("classList"in document.createElement("a"))for(var t=0,n=n=r.blacklistedBrowsers;;){if(t>=n.length)break;n[t++].test(navigator.userAgent)&&(e=!1)}else e=!1;else e=!1;return e},r.dataURItoBlob=function(e){for(var t=atob(e.split(",")[1]),n=e.split(",")[0].split(":")[1].split(";")[0],i=new ArrayBuffer(t.length),o=new Uint8Array(i),r=0,s=t.length,a=0<=s;a?r<=s:r>=s;a?r++:r--)o[r]=t.charCodeAt(r);return new Blob([i],{type:n})};var s=function(e,t){return e.filter(function(e){return e!==t}).map(function(e){return e})},a=function(e){return e.replace(/[\-_](\w)/g,function(e){return e.charAt(1).toUpperCase()})};r.createElement=function(e){var t=document.createElement("div");return t.innerHTML=e,t.childNodes[0]},r.elementInside=function(e,t){if(e===t)return!0;for(;e=e.parentNode;)if(e===t)return!0;return!1},r.getElement=function(e,t){var n=void 0;if("string"==typeof e?n=document.querySelector(e):null!=e.nodeType&&(n=e),null==n)throw new Error("Invalid `"+t+"` option provided. Please provide a CSS selector or a plain HTML element.");return n},r.getElements=function(e,t){var n=void 0,i=void 0;if(e instanceof Array){i=[];try{for(var o=0,r=r=e;!(o>=r.length);)n=r[o++],i.push(this.getElement(n,t))}catch(e){i=null}}else if("string"==typeof e){i=[];for(var s=0,a=a=document.querySelectorAll(e);!(s>=a.length);)n=a[s++],i.push(n)}else null!=e.nodeType&&(i=[e]);if(null==i||!i.length)throw new Error("Invalid `"+t+"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");return i},r.confirm=function(e,t,n){return window.confirm(e)?t():null!=n?n():void 0},r.isValidFile=function(e,t){if(!t)return!0;t=t.split(",");for(var n=e.type,i=n.replace(/\/.*$/,""),o=0,r=r=t;;){if(o>=r.length)break;var s=r[o++];if("."===(s=s.trim()).charAt(0)){if(-1!==e.name.toLowerCase().indexOf(s.toLowerCase(),e.name.length-s.length))return!0}else if(/\/\*$/.test(s)){if(i===s.replace(/\/.*$/,""))return!0}else if(n===s)return!0}return!1},"undefined"!=typeof jQuery&&null!==jQuery&&(jQuery.fn.dropzone=function(e){return this.each(function(){return new r(this,e)})}),void 0!==e&&null!==e?e.exports=r:window.Dropzone=r,r.ADDED="added",r.QUEUED="queued",r.ACCEPTED=r.QUEUED,r.UPLOADING="uploading",r.PROCESSING=r.UPLOADING,r.CANCELED="canceled",r.ERROR="error",r.SUCCESS="success";var l=function(e,t,n,i,o,r,s,a,l,u){var c=function(e){e.naturalWidth;var t=e.naturalHeight,n=document.createElement("canvas");n.width=1,n.height=t;var i=n.getContext("2d");i.drawImage(e,0,0);for(var o=i.getImageData(1,0,1,t).data,r=0,s=t,a=t;a>r;)0===o[4*(a-1)+3]?s=a:r=a,a=s+r>>1;var l=a/t;return 0===l?1:l}(t);return e.drawImage(t,n,i,o,r,s,a,l,u/c)},u=function(){function e(){i(this,e)}return t(e,null,[{key:"initClass",value:function(){this.KEY_STR="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}},{key:"encode64",value:function(e){for(var t="",n=void 0,i=void 0,o="",r=void 0,s=void 0,a=void 0,l="",u=0;r=(n=e[u++])>>2,s=(3&n)<<4|(i=e[u++])>>4,a=(15&i)<<2|(o=e[u++])>>6,l=63&o,isNaN(i)?a=l=64:isNaN(o)&&(l=64),t=t+this.KEY_STR.charAt(r)+this.KEY_STR.charAt(s)+this.KEY_STR.charAt(a)+this.KEY_STR.charAt(l),n=i=o="",r=s=a=l="",ue.length)break}return n}},{key:"decode64",value:function(e){var t=void 0,n=void 0,i="",o=void 0,r=void 0,s="",a=0,l=[];for(/[^A-Za-z0-9\+\/\=]/g.exec(e)&&console.warn("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding."),e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");t=this.KEY_STR.indexOf(e.charAt(a++))<<2|(o=this.KEY_STR.indexOf(e.charAt(a++)))>>4,n=(15&o)<<4|(r=this.KEY_STR.indexOf(e.charAt(a++)))>>2,i=(3&r)<<6|(s=this.KEY_STR.indexOf(e.charAt(a++))),l.push(t),64!==r&&l.push(n),64!==s&&l.push(i),t=n=i="",o=r=s="",a'}},function(e,t){e.exports={title:"separator",icon:""}},function(e,t,n){"use strict";n.d(t,"a",function(){return i}),n.d(t,"b",function(){return o});var i=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"editr"},[n("div",{staticClass:"editr--toolbar"},e._l(e.modules,function(t,i){return n("Btn",{key:t.title+i,ref:"btn-"+t.title,refInFor:!0,attrs:{module:t,options:e.mergedOptions,title:t.description||""}})})),n("div",{ref:"content",staticClass:"editr--content",attrs:{contenteditable:"true",tabindex:"1",placeholder:e.placeholder}})])},o=[]}])}); diff --git a/server/.env-template b/server/.env-template new file mode 100644 index 0000000..64ccf65 --- /dev/null +++ b/server/.env-template @@ -0,0 +1,5 @@ +PORT= +SECRET= +CLOUD_BUCKET= +GCLOUD_PROJECT= +KEYFILE_PATH= \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..2626976 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env +keyfile.json \ No newline at end of file diff --git a/server/app.js b/server/app.js new file mode 100644 index 0000000..b85e708 --- /dev/null +++ b/server/app.js @@ -0,0 +1,25 @@ +require('dotenv').config(); +// depedencies +const express = require('express'); +const logger = require('morgan'); +const mongoose = require('mongoose'); +const routes = require('./routes/index'); + +// create express instance +const app = express(); + +// connect to database +mongoose.connect('mongodb://localhost:27017/mini-wp', { useNewUrlParser: true }); + +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); + +app.use(logger('dev')); +app.use(require('cors')()); + +app.use('/', routes); + +const port = process.env.PORT || 3000; +app.listen(port, function () { + console.log('App listening on port', port); +}); diff --git a/server/controllers/articleController.js b/server/controllers/articleController.js new file mode 100644 index 0000000..b036672 --- /dev/null +++ b/server/controllers/articleController.js @@ -0,0 +1,106 @@ +const Article = require('../models/articleModel'); + +module.exports = { + getAllArticle: function (req, res) { + Article + .find({}).populate('author', '-password').sort('-created_at') + .then((data) => { + res.status(200).json({ + result: data, + error: null + }) + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + }, + myArticle: function (req, res) { + Article + .find({ author: req.auth_user._id }).sort('-created_at') + .populate('author', '-password') + .then((data) => { + res.status(200).json({ + result: data, + error: null + }) + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + }, + createNewArticle: function (req, res) { + console.log(req.file); + Article + .create({ + author: req.auth_user._id, + title: req.body.title, + description: req.body.description, + content: req.body.content, + featured_image: req.file.cloudStoragePublicUrl + }) + .then((data) => { + res.status(201).json({ + result: { + data, + message: 'Successfully created new article' + }, + error: null + }) + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + }, + updateArticle: function (req, res) { + Article + .findByIdAndUpdate( + req.params.id, + { + title: req.body.title, + description: req.body.description, + content: req.body.content + }) + .then((data) => { + res.status(200).json({ + result: { + data, + message: 'Successfully update article' + }, + error: null, + }) + }).catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + }, + deleteArticle: function (req, res) { + Article + .findByIdAndDelete(req.params.id) + .then((data) => { + res.status(200).json({ + result: { + data, + message: 'Successfully delete article' + }, + error: null, + }) + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + } +}; diff --git a/server/controllers/userController.js b/server/controllers/userController.js new file mode 100644 index 0000000..a56fed8 --- /dev/null +++ b/server/controllers/userController.js @@ -0,0 +1,92 @@ +const User = require('../models/userModel'); +const { decrypt } = require('../helpers/encrypt'); +const jwt = require('jsonwebtoken'); + +module.exports = { + signup: function (req, res) { + User + .create({ + name: req.body.name, + email: req.body.email, + password: req.body.password + }) + .then((user) => { + res.status(201).json({ + result: { + user, + message: 'Successfully register account' + }, + error: null + }); + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + }, + signin: function (req, res) { + User + .findOne({ email: req.body.email }) + .then((data) => { + if (data) { + const isValid = decrypt(req.body.password, data.password); + if (isValid) { + const access_token = jwt.sign({ data }, process.env.SECRET); + res.status(200).json({ + result: { + access_token, + message: 'Successfully logged in' + } + }) + } else { + res.status(404).json({ + result: null, + error: { + message: 'Wrong Password' + } + }); + } + } else { + res.status(404).json({ + result: null, + error: { + message: 'Email is not registered' + } + }) + } + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }); + }); + }, + checkUser: function (req, res) { + User + .findById(req.auth_user._id).select('-password') + .then((data) => { + if (data) { + res.status(200).json({ + result: data, + error: null + }); + } else { + res.status(404).json({ + result: null, + error: { + message: 'You must login first' + } + }) + } + }) + .catch((err) => { + res.status(500).json({ + result: null, + error: err + }) + }); + } +}; diff --git a/server/db.json b/server/db.json new file mode 100644 index 0000000..fd0c7d0 --- /dev/null +++ b/server/db.json @@ -0,0 +1,28 @@ +{ + "myArticles": [ + { + "title": "Nasi Goreng Sate", + "description": "Kuliner", + "contents": "Lorem ipsum dolor sit amet, no qui debet putant sententiae, in sumo eros ius, usu purto eius in. No vix impetus graecis voluptua, sed et indoctum omittantur", + "id": 1 + }, + { + "title": "Nasi Uduk Depan", + "description": "Kuliner", + "contents": "Lorem ipsum dolor sit amet, no qui debet putant sententiae, in sumo eros ius, usu purto eius in. No vix impetus graecis voluptua, sed et indoctum omittantur", + "id": 2 + }, + { + "title": "Mie Goreng", + "description": "Lifestyle", + "contents": "Lorem ipsum dolor sit amet, no qui debet putant sententiae, in sumo eros ius, usu purto eius in. No vix impetus graecis voluptua, sed et indoctum omittantur", + "id": 3 + }, + { + "title": "Mie Aceh Special", + "description": "Lifestyle", + "contents": "Lorem ipsum dolor sit amet, no qui debet putant sententiae, in sumo eros ius, usu purto eius in. No vix impetus graecis voluptua, sed et indoctum omittantur", + "id": 4 + } + ] +} \ No newline at end of file diff --git a/server/helpers/encrypt.js b/server/helpers/encrypt.js new file mode 100644 index 0000000..c41c29a --- /dev/null +++ b/server/helpers/encrypt.js @@ -0,0 +1,11 @@ +const bcrypt = require('bcrypt'); +const salt = bcrypt.genSaltSync(10) + +module.exports = { + encrypt: function (password) { + return bcrypt.hashSync(password, salt); + }, + decrypt: function(password, input_pwd) { + return bcrypt.compareSync(password, input_pwd); + } +}; diff --git a/server/helpers/images.js b/server/helpers/images.js new file mode 100644 index 0000000..4a57616 --- /dev/null +++ b/server/helpers/images.js @@ -0,0 +1,55 @@ +const { Storage } = require('@google-cloud/storage') + +const CLOUD_BUCKET = process.env.CLOUD_BUCKET + +const storage = new Storage({ + projectId: process.env.GCLOUD_PROJECT, + keyFilename: process.env.KEYFILE_PATH +}) +const bucket = storage.bucket(CLOUD_BUCKET) + +const getPublicUrl = (filename) => { + return `https://storage.googleapis.com/${CLOUD_BUCKET}/${filename}` +} + +const sendUploadToGCS = (req, res, next) => { + if (!req.file) { + return next() + } + + const gcsname = Date.now() + req.file.originalname + const file = bucket.file(gcsname) + + const stream = file.createWriteStream({ + metadata: { + contentType: req.file.mimetype + }, + resumable: false + }) + + stream.on('error', (err) => { + req.file.cloudStorageError = err + next(err) + }) + + stream.on('finish', () => { + req.file.cloudStorageObject = gcsname + file.makePublic().then(() => { + req.file.cloudStoragePublicUrl = getPublicUrl(gcsname) + next() + }) + }) + + stream.end(req.file.buffer) +} + +const Multer = require('multer'), + multer = Multer({ + storage: Multer.MemoryStorage + }) + +module.exports = { + getPublicUrl, + sendUploadToGCS, + multer +} \ No newline at end of file diff --git a/server/middlewares/authUser.js b/server/middlewares/authUser.js new file mode 100644 index 0000000..86777c2 --- /dev/null +++ b/server/middlewares/authUser.js @@ -0,0 +1,30 @@ +const User = require('../models/userModel'); +const jwt = require('jsonwebtoken'); + +module.exports = { + isLogin: function (req, res, next) { + // console.log(req.headers.token); + try { + const { data } = jwt.verify(req.headers.token, process.env.SECRET); + if (data) { + User + .findOne({ email: data.email }) + .then((data) => { + // console.log(data); + req.auth_user = data; + next(); + }) + .catch((err) => { + res.status(400).json({ + message: 'No such user' + }) + }); + } + } + catch (error) { + res.status(401).json({ + message: 'Invalid token' + }) + } + } +}; diff --git a/server/models/articleModel.js b/server/models/articleModel.js new file mode 100644 index 0000000..57c198d --- /dev/null +++ b/server/models/articleModel.js @@ -0,0 +1,14 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const articleSchema = new Schema({ + title: String, + description: String, + content: String, + author: { type: Schema.Types.ObjectId, ref: 'User' }, + created_at: { type: Date, default: new Date() }, + featured_image: String +}); + +const Article = mongoose.model('Article', articleSchema); +module.exports = Article; diff --git a/server/models/userModel.js b/server/models/userModel.js new file mode 100644 index 0000000..e86a693 --- /dev/null +++ b/server/models/userModel.js @@ -0,0 +1,44 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const { encrypt } = require('../helpers/encrypt'); + +const userSchema = new Schema({ + name: { + type: String, + required: [true, 'Please input your name'] + }, + email: { + type: String, + validate: { + isAsync: true, + validator: function (value) { + return new Promise((resolve, reject) => { + User + .findOne({ email: value, _id: { $ne: this._id } }) + .then((found) => { + if (found) { + reject(false); + } else { + resolve(true); + } + }).catch((err) => { + reject(err); + }); + }); + }, + message: 'Email already exists!' + } + }, + password: { + type: String, + required: [true, 'Password cannot be empty'] + } +}); + +userSchema.pre('save', function (next) { + this.password = encrypt(this.password); + next(); +}); + +const User = mongoose.model('User', userSchema); +module.exports = User; \ No newline at end of file diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..dd22ef2 --- /dev/null +++ b/server/package.json @@ -0,0 +1,26 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "mini-wp", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node app.js", + "dev": "nodemon app.js" + }, + "keywords": [], + "author": "Arief Rachman", + "license": "ISC", + "dependencies": { + "@google-cloud/storage": "^2.4.2", + "axios": "^0.18.0", + "bcrypt": "^3.0.4", + "cors": "^2.8.5", + "dotenv": "^6.2.0", + "express": "^4.16.4", + "jsonwebtoken": "^8.4.0", + "mongoose": "^5.4.13", + "morgan": "^1.9.1", + "multer": "^1.4.1" + } +} diff --git a/server/routes/index.js b/server/routes/index.js new file mode 100644 index 0000000..8d8404b --- /dev/null +++ b/server/routes/index.js @@ -0,0 +1,22 @@ +const routes = require('express').Router(); +const { signin, signup, checkUser } = require('../controllers/userController'); +const { getAllArticle, myArticle, createNewArticle, updateArticle, deleteArticle } = require('../controllers/articleController'); +const images = require('../helpers/images'); +const { isLogin } = require('../middlewares/authUser'); + +routes.get('/', (req, res) => { + res.json({ message: 'Connected' }); +}); + +routes.get('/articles/discover', getAllArticle); + +routes.post('/articles', isLogin, images.multer.single('file'), images.sendUploadToGCS, createNewArticle); +routes.get('/articles/myArticles', isLogin, myArticle); +routes.put('/articles/:id', isLogin, updateArticle); +routes.delete('/articles/:id', isLogin, deleteArticle); + +routes.post('/user/signup', signup); +routes.post('/user/signin', signin); +routes.get('/user/auth', isLogin, checkUser); + +module.exports = routes; \ No newline at end of file