Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
Commit 3f2cfa3d authored by Pierre Jarriges's avatar Pierre Jarriges
Browse files

wip admin manage files

parent 52dd27f2
No related branches found
No related tags found
1 merge request!9Upload files
Showing
with 174 additions and 70 deletions
"use strict"; "use strict";
const Article = require("../article"); const Article = require("../article");
const { images_url } = require("../constants");
const { fetch_post_article, fetch_article, fetch_update_article } = require("../xhr"); const { fetch_post_article, fetch_article, fetch_update_article } = require("../xhr");
class CreateArticleForm { class CreateArticleForm {
...@@ -23,6 +22,7 @@ class CreateArticleForm { ...@@ -23,6 +22,7 @@ class CreateArticleForm {
const metadata = Object.assign(this.state.output.metadata, { const metadata = Object.assign(this.state.output.metadata, {
[field]: e.target.value, [field]: e.target.value,
}); });
this.state.output.metadata = metadata; this.state.output.metadata = metadata;
} }
...@@ -166,22 +166,26 @@ class CreateArticleForm { ...@@ -166,22 +166,26 @@ class CreateArticleForm {
{ {
tag: "img", tag: "img",
style_rules: { minWidth: "100%", minHeight: "100%" }, style_rules: { minWidth: "100%", minHeight: "100%" },
src: img ? `${images_url}/${img}` : "", src: img || "",
} }
], ],
}, },
{ {
tag: "input", tag: "select",
type: "text", contents: [
placeholder: "image file name", { tag: "option", value: "", contents: "choose", disabled: true, selected: !this.state.output.images[i] }
value: img, ].concat((this.params.files_index.images || []).map(url => {
oninput: e => { return {
tag: "option",
value: url,
selected: this.state.output.images[i] && this.state.output.images[i] === url,
contents: url.split("/").reverse()[0],
}
})),
onchange: e => {
this.state.output.images[i] = e.target.value; this.state.output.images[i] = e.target.value;
} this.refresh_images();
}, },
{
tag: "button", contents: "OK",
onclick: this.refresh_images.bind(this)
}, },
{ {
tag: "button", contents: "DEL", tag: "button", contents: "DEL",
...@@ -234,7 +238,7 @@ class CreateArticleForm { ...@@ -234,7 +238,7 @@ class CreateArticleForm {
return { return {
tag: "img", tag: "img",
style_rules: { height: "100px", width: "auto" }, style_rules: { height: "100px", width: "auto" },
src: `${images_url}/${img}` src: img
} }
}) })
}, },
......
"use strict";
const { fetch_post_file } = require("../xhr");
class ManageFilesForm {
constructor(params) {
this.params = params;
}
render_index_view() {
return {
tag: "div",
contents: Object.entries(this.params.files_index).map(entry => {
const [category, urls] = entry;
return {
tag: "div",
contents: [
{
tag: "h3",
contents: category
}
].concat(urls.map(url => {
return { tag: "div", contents: url }
}))
}
})
}
}
render() {
return {
tag: "div",
contents: [
this.render_index_view(),
{
tag: "form",
style_rules: { border: "1px solid black" },
enctype: "multipart/form-data",
onsubmit: function (e) {
e.preventDefault();
fetch_post_file(e.target).then(res => console.log(res)).catch(err => console.log(err));
},
contents: [
{ tag: "input", name: "file", type: "file", multiple: true },
{ tag: "input", type: "submit" }
]
}
]
}
}
}
module.exports = ManageFilesForm;
\ No newline at end of file
const { fetch_post_file } = require("../xhr"); const { fetch_post_file, fetch_static_files_index } = require("../xhr");
const CreateArticleForm = require("./create-article-form"); const CreateArticleForm = require("./create-article-form");
const ManageFilesForm = require("./manage-files-form");
const UpdateArticleForm = require("./update-article-form"); const UpdateArticleForm = require("./update-article-form");
class RootComponent { class RootComponent {
constructor() { constructor() {
this.state = { this.state = {
selected_tab: "" selected_tab: "",
loading_index: true,
static_files_index: {}
}; };
this.fetch_files_index();
}
build_index_object(files_urls) {
return files_urls.reduce((o, url) => {
const split_url = url.split("/");
const cat = split_url[(split_url[2] === "uploads") ? 3 : 2];
o[cat] = o[cat] ? [...o[cat], url] : [url];
return o;
}, {});
}
fetch_files_index() {
fetch_static_files_index()
.then(files =>
this.state.static_files_index = this.build_index_object(files)
)
.catch(err => console.log(err))
.finally(() => {
this.state.loading_index = false;
obj2htm.renderCycle();
});
} }
handle_nav_click(e) { handle_nav_click(e) {
...@@ -16,11 +42,14 @@ class RootComponent { ...@@ -16,11 +42,14 @@ class RootComponent {
} }
render_state() { render_state() {
const files_index = this.state.static_files_index;
switch (this.state.selected_tab) { switch (this.state.selected_tab) {
case "create": case "create":
return new CreateArticleForm().render(); return new CreateArticleForm({ files_index }).render();
case "update": case "update":
return new UpdateArticleForm().render(); return new UpdateArticleForm({ files_index }).render();
case "files":
return new ManageFilesForm({ files_index }).render()
default: default:
return undefined; return undefined;
} }
...@@ -31,19 +60,6 @@ class RootComponent { ...@@ -31,19 +60,6 @@ class RootComponent {
tag: "main", tag: "main",
contents: [ contents: [
{ tag: "h1", contents: "Kuadrado admin panel" }, { tag: "h1", contents: "Kuadrado admin panel" },
{
tag: "form",
style_rules: { border: "1px solid black" },
enctype: "multipart/form-data",
onsubmit: function (e) {
e.preventDefault();
fetch_post_file(e.target).then(res => console.log(res)).catch(err => console.log(err));
},
contents: [
{ tag: "input", name: "file", type: "file", multiple: true },
{ tag: "input", type: "submit" }
]
},
{ {
tag: "nav", tag: "nav",
contents: [ contents: [
...@@ -57,6 +73,11 @@ class RootComponent { ...@@ -57,6 +73,11 @@ class RootComponent {
class: this.state.selected_tab === "update" ? "selected" : "", class: this.state.selected_tab === "update" ? "selected" : "",
onclick: this.handle_nav_click.bind(this), onclick: this.handle_nav_click.bind(this),
}, },
{
tag: "span", contents: "Manage files", tab_name: "files",
class: this.state.selected_tab === "files" ? "selected" : "",
onclick: this.handle_nav_click.bind(this),
},
], ],
}, },
this.render_state(), this.render_state(),
......
...@@ -5,7 +5,8 @@ const ArticleList = require("./articles-list"); ...@@ -5,7 +5,8 @@ const ArticleList = require("./articles-list");
const CreateArticleForm = require("./create-article-form"); const CreateArticleForm = require("./create-article-form");
class UpdateArticleForm { class UpdateArticleForm {
constructor() { constructor(params) {
this.params = params;
this.state = { this.state = {
search_article_title: "", search_article_title: "",
article_to_update: {}, article_to_update: {},
...@@ -63,7 +64,8 @@ class UpdateArticleForm { ...@@ -63,7 +64,8 @@ class UpdateArticleForm {
on_article_sent: () => { on_article_sent: () => {
this.reset(); this.reset();
this.articles_list.fetch_list(); this.articles_list.fetch_list();
} },
...this.params
}).render()] }).render()]
: [] : []
} }
......
...@@ -115,11 +115,23 @@ function fetch_post_file(form) { ...@@ -115,11 +115,23 @@ function fetch_post_file(form) {
} else { } else {
resolve((await res.json())); resolve((await res.json()));
} }
}) }).catch(err => reject(err))
}) })
} }
function fetch_static_files_index() {
return new Promise((resolve, reject) => {
fetch("/static-files-index").then(async res => {
if (res.status >= 400 && res.status < 600) {
reject((await res.text()))
} else {
resolve((await res.json()));
}
}).catch(err => reject(err))
})
}
module.exports = { module.exports = {
fetch_article, fetch_article,
...@@ -129,4 +141,5 @@ module.exports = { ...@@ -129,4 +141,5 @@ module.exports = {
fetch_delete_article, fetch_delete_article,
fetch_all_articles, fetch_all_articles,
fetch_post_file, fetch_post_file,
fetch_static_files_index,
} }
\ No newline at end of file
...@@ -7,12 +7,7 @@ db.auth(adminname, adminpwd); ...@@ -7,12 +7,7 @@ db.auth(adminname, adminpwd);
articles = db.getCollection("articles"); articles = db.getCollection("articles");
articles.update({}, articles.find().forEach(art => {
{ art.images = art.images.map(img => "/assets/images/" + img);
$set: { articles.save(art);
"metadata": { "description": "" }, });
"with_static_view": false \ No newline at end of file
}
},
{ upsert: false, multi: true }
);
\ No newline at end of file
...@@ -39,7 +39,7 @@ impl StaticFilesIndex { ...@@ -39,7 +39,7 @@ impl StaticFilesIndex {
fn _push_path(path: &Path, files: &mut Vec<String>, strip_from: &Path) { fn _push_path(path: &Path, files: &mut Vec<String>, strip_from: &Path) {
let push_path = path.strip_prefix(strip_from).unwrap(); let push_path = path.strip_prefix(strip_from).unwrap();
files.push(push_path.to_str().unwrap().to_owned()); files.push(format!("/{}", push_path.to_str().unwrap().to_owned()));
} }
pub fn rebuild(&mut self, env: &Env) { pub fn rebuild(&mut self, env: &Env) {
...@@ -54,6 +54,10 @@ impl StaticFilesIndex { ...@@ -54,6 +54,10 @@ impl StaticFilesIndex {
let strip_from = StaticFilesIndex::get_public_dir(env); let strip_from = StaticFilesIndex::get_public_dir(env);
StaticFilesIndex::_push_path(path, &mut self.0, &strip_from); StaticFilesIndex::_push_path(path, &mut self.0, &strip_from);
} }
pub fn get_index(&self) -> Vec<String> {
self.0.clone()
}
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
......
...@@ -95,6 +95,7 @@ async fn main() -> std::io::Result<()> { ...@@ -95,6 +95,7 @@ async fn main() -> std::io::Result<()> {
.service(get_article) .service(get_article)
.service(get_all_articles) .service(get_all_articles)
.service(post_files) .service(post_files)
.service(get_static_files_index)
///////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// STANDARD FILES /////////////////////////////////////////////////////////////////////////////////////////// // STANDARD FILES ///////////////////////////////////////////////////////////////////////////////////////////
.service(resource("/favicon.ico").route(get().to(favicon))) .service(resource("/favicon.ico").route(get().to(favicon)))
......
use crate::{core::static_files::*, middleware::AuthenticatedAdminMiddleware, AppState}; use crate::{core::static_files::*, middleware::AuthenticatedAdminMiddleware, AppState};
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_web::{post, web::Data, HttpRequest, HttpResponse, Responder}; use actix_web::{get, post, web::Data, HttpRequest, HttpResponse, Responder};
use futures::StreamExt; use futures::StreamExt;
use std::{ use std::{
fs::{remove_file, File}, fs::{remove_file, File},
...@@ -106,6 +106,18 @@ pub async fn post_files( ...@@ -106,6 +106,18 @@ pub async fn post_files(
HttpResponse::Ok().json(uploaded_filepathes) HttpResponse::Ok().json(uploaded_filepathes)
} }
#[get("/static-files-index")]
async fn get_static_files_index(
static_files_index: Data<std::sync::Mutex<StaticFilesIndex>>,
) -> impl Responder {
HttpResponse::Ok().json(
static_files_index
.lock()
.expect("Couldn't lock files index")
.get_index(),
)
}
// EXAMPLE FROM ACTIX REPO (using threadpool) // EXAMPLE FROM ACTIX REPO (using threadpool)
// use futures::TryStreamExt; // use futures::TryStreamExt;
......
{ {
"name": "kuadrado-website", "name": "kuadrado-website",
"version": "1.0.4", "version": "1.1.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "kuadrado-website", "name": "kuadrado-website",
"version": "1.0.4", "version": "1.1.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ks-cheap-translator": "^0.1.0", "ks-cheap-translator": "^0.1.0",
......
...@@ -4,7 +4,6 @@ const ImageCarousel = require("../generic-components/image-carousel"); ...@@ -4,7 +4,6 @@ const ImageCarousel = require("../generic-components/image-carousel");
const { getArticleBody } = require("../lib/article-utils"); const { getArticleBody } = require("../lib/article-utils");
const { fetch_json_or_error_text } = require("../lib/fetch"); const { fetch_json_or_error_text } = require("../lib/fetch");
const { MentaloEngine } = require("mentalo-engine"); const { MentaloEngine } = require("mentalo-engine");
const { images_url, data_url } = require("../../constants");
class GameArticle { class GameArticle {
...@@ -41,16 +40,16 @@ class GameArticle { ...@@ -41,16 +40,16 @@ class GameArticle {
tag: "button", tag: "button",
class: "play-button", class: "play-button",
contents: "▶️&nbsp;&nbsp;" + t("Jouer"), contents: "▶️&nbsp;&nbsp;" + t("Jouer"),
onclick: this.handle_click_play.bind(this, button_data.filename, button_data.engine) onclick: this.handle_click_play.bind(this, button_data.fileurl, button_data.engine)
}; };
} }
load_and_run_mentalo_game(filename, button_element) { load_and_run_mentalo_game(fileurl, button_element) {
const button_text = button_element.innerHTML; const button_text = button_element.innerHTML;
button_element.innerHTML = "Loading ..."; button_element.innerHTML = "Loading ...";
button_element.style.pointerEvents = "none"; button_element.style.pointerEvents = "none";
fetch_json_or_error_text(`${data_url}/${filename}`) fetch_json_or_error_text(fileurl)
.then(game_data => { .then(game_data => {
const container = document.createElement("div"); const container = document.createElement("div");
container.style.position = "fixed"; container.style.position = "fixed";
...@@ -88,10 +87,10 @@ class GameArticle { ...@@ -88,10 +87,10 @@ class GameArticle {
}); });
} }
handle_click_play(filename, engine, e) { handle_click_play(fileurl, engine, e) {
switch (engine) { switch (engine) {
case "mentalo": case "mentalo":
this.load_and_run_mentalo_game(filename, e.target); this.load_and_run_mentalo_game(fileurl, e.target);
break; break;
default: default:
console.log("Error, unkown engine") console.log("Error, unkown engine")
...@@ -116,7 +115,7 @@ class GameArticle { ...@@ -116,7 +115,7 @@ class GameArticle {
tag: "div", tag: "div",
id: "article-banner", id: "article-banner",
style_rules: { style_rules: {
backgroundImage: `url(${images_url}/${images[0]})`, backgroundImage: `url(${images[0]})`,
}, },
contents: [ contents: [
{ {
...@@ -158,7 +157,7 @@ class GameArticle { ...@@ -158,7 +157,7 @@ class GameArticle {
}, },
], ],
}, },
trad_ready && new ImageCarousel({ images: images.map(img => `${images_url}/${img}`) }).render(), trad_ready && new ImageCarousel({ images }).render(),
this.details.length > 0 && { this.details.length > 0 && {
tag: "div", tag: "div",
class: "article-details", class: "article-details",
......
"use strict"; "use strict";
const ImageCarousel = require("../generic-components/image-carousel"); const ImageCarousel = require("../generic-components/image-carousel");
const { images_url } = require("../../constants");
const { getArticleBody } = require("../lib/article-utils"); const { getArticleBody } = require("../lib/article-utils");
...@@ -35,7 +34,7 @@ class SoftwareArticle { ...@@ -35,7 +34,7 @@ class SoftwareArticle {
contents: [ contents: [
{ {
tag: "img", tag: "img",
src: `${images_url}/${logo}` src: logo
}, },
] ]
}, },
...@@ -76,7 +75,7 @@ class SoftwareArticle { ...@@ -76,7 +75,7 @@ class SoftwareArticle {
tag: "div", tag: "div",
class: "article-more", class: "article-more",
contents: [ contents: [
trad_ready && screens.length > 0 && new ImageCarousel({ images: screens.map(img => `${images_url}/${img}`) }).render(), trad_ready && screens.length > 0 && new ImageCarousel({ images: screens }).render(),
details.length > 0 && { details.length > 0 && {
tag: "div", tag: "div",
class: "article-details", class: "article-details",
......
"use strict"; "use strict";
const { images_url } = require("../../constants");
class ThemeCard { class ThemeCard {
constructor(props) { constructor(props) {
this.props = props; this.props = props;
...@@ -16,7 +14,12 @@ class ThemeCard { ...@@ -16,7 +14,12 @@ class ThemeCard {
{ {
tag: "div", tag: "div",
class: "card-img", class: "card-img",
contents: [{ tag: "img", alt: `thematic image ${this.props.img.replace(/\.[A-Za-z]+/, "")}`, src: `${images_url}/${this.props.img}` }], contents: [
{
tag: "img",
alt: `thematic image ${this.props.img.split("/").reverse()[0].replace(/\.[A-Za-z]+/, "")}`,
src: this.props.img
}],
}, },
{ {
tag: "div", tag: "div",
......
...@@ -59,20 +59,20 @@ class HomePage extends WebPage { ...@@ -59,20 +59,20 @@ class HomePage extends WebPage {
contents: [ contents: [
{ {
title: t("Jeux"), title: t("Jeux"),
img: "game_controller.svg", img: images_url + "/game_controller.svg",
href: "/games/", href: "/games/",
description: description:
t("games-description"), t("games-description"),
}, },
{ {
title: t("Pédagogie"), title: t("Pédagogie"),
img: "brain.svg", img: images_url + "/brain.svg",
href: "/education/", href: "/education/",
description: t("education-description"), description: t("education-description"),
}, },
{ {
title: "Software", title: "Software",
img: "meca_proc.svg", img: images_url + "/meca_proc.svg",
href: "/software-development/", href: "/software-development/",
description: t("software-description"), description: t("software-description"),
}, },
......
"use strict"; "use strict";
const { images_url } = require("../../../../constants");
const ImageCarousel = require("../../../generic-components/image-carousel"); const ImageCarousel = require("../../../generic-components/image-carousel");
const { getArticleBody } = require("../../../lib/article-utils"); const { getArticleBody } = require("../../../lib/article-utils");
...@@ -27,7 +26,7 @@ class EduArticle { ...@@ -27,7 +26,7 @@ class EduArticle {
tag: "div", class: "edu-art-image", tag: "div", class: "edu-art-image",
contents: [ contents: [
{ {
tag: "img", src: `${images_url}/${images[0]}` tag: "img", src: images[0]
} }
] ]
}, },
...@@ -45,7 +44,7 @@ class EduArticle { ...@@ -45,7 +44,7 @@ class EduArticle {
}, },
images.length > 1 && { images.length > 1 && {
tag: "div", class: "edu-art-carousel", contents: [ tag: "div", class: "edu-art-carousel", contents: [
new ImageCarousel({ images: images.map(img => `${images_url}/${img}`) }).render() new ImageCarousel({ images: images }).render()
] ]
}, },
details.length > 0 && { details.length > 0 && {
......
"use strict"; "use strict";
const { images_url } = require("../../../../../admin-frontend/src/constants");
class GameThumb { class GameThumb {
constructor(props) { constructor(props) {
...@@ -21,7 +20,7 @@ class GameThumb { ...@@ -21,7 +20,7 @@ class GameThumb {
target: "_blank", target: "_blank",
class: "game-thumb", class: "game-thumb",
style_rules: { style_rules: {
backgroundImage: `url(${images_url}/${images[0]})`, backgroundImage: `url(${images[0]})`,
}, },
contents: [ contents: [
{ {
......
"use strict"; "use strict";
const { images_url } = require("../../../../../admin-frontend/src/constants");
class SoftwareThumb { class SoftwareThumb {
constructor(props) { constructor(props) {
...@@ -26,7 +25,7 @@ class SoftwareThumb { ...@@ -26,7 +25,7 @@ class SoftwareThumb {
class: "software-image", class: "software-image",
contents: [ contents: [
{ {
tag: "img", src: `${images_url}/${images[0]}` tag: "img", src: `${images[0]}`
} }
] ]
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment