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";
const Article = require("../article");
const { images_url } = require("../constants");
const { fetch_post_article, fetch_article, fetch_update_article } = require("../xhr");
class CreateArticleForm {
......@@ -23,6 +22,7 @@ class CreateArticleForm {
const metadata = Object.assign(this.state.output.metadata, {
[field]: e.target.value,
});
this.state.output.metadata = metadata;
}
......@@ -166,22 +166,26 @@ class CreateArticleForm {
{
tag: "img",
style_rules: { minWidth: "100%", minHeight: "100%" },
src: img ? `${images_url}/${img}` : "",
src: img || "",
}
],
},
{
tag: "input",
type: "text",
placeholder: "image file name",
value: img,
oninput: e => {
tag: "select",
contents: [
{ tag: "option", value: "", contents: "choose", disabled: true, selected: !this.state.output.images[i] }
].concat((this.params.files_index.images || []).map(url => {
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;
}
},
{
tag: "button", contents: "OK",
onclick: this.refresh_images.bind(this)
this.refresh_images();
},
},
{
tag: "button", contents: "DEL",
......@@ -234,7 +238,7 @@ class CreateArticleForm {
return {
tag: "img",
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 ManageFilesForm = require("./manage-files-form");
const UpdateArticleForm = require("./update-article-form");
class RootComponent {
constructor() {
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) {
......@@ -16,11 +42,14 @@ class RootComponent {
}
render_state() {
const files_index = this.state.static_files_index;
switch (this.state.selected_tab) {
case "create":
return new CreateArticleForm().render();
return new CreateArticleForm({ files_index }).render();
case "update":
return new UpdateArticleForm().render();
return new UpdateArticleForm({ files_index }).render();
case "files":
return new ManageFilesForm({ files_index }).render()
default:
return undefined;
}
......@@ -31,19 +60,6 @@ class RootComponent {
tag: "main",
contents: [
{ 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",
contents: [
......@@ -57,6 +73,11 @@ class RootComponent {
class: this.state.selected_tab === "update" ? "selected" : "",
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(),
......
......@@ -5,7 +5,8 @@ const ArticleList = require("./articles-list");
const CreateArticleForm = require("./create-article-form");
class UpdateArticleForm {
constructor() {
constructor(params) {
this.params = params;
this.state = {
search_article_title: "",
article_to_update: {},
......@@ -63,7 +64,8 @@ class UpdateArticleForm {
on_article_sent: () => {
this.reset();
this.articles_list.fetch_list();
}
},
...this.params
}).render()]
: []
}
......
......@@ -115,11 +115,23 @@ function fetch_post_file(form) {
} else {
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 = {
fetch_article,
......@@ -129,4 +141,5 @@ module.exports = {
fetch_delete_article,
fetch_all_articles,
fetch_post_file,
fetch_static_files_index,
}
\ No newline at end of file
......@@ -7,12 +7,7 @@ db.auth(adminname, adminpwd);
articles = db.getCollection("articles");
articles.update({},
{
$set: {
"metadata": { "description": "" },
"with_static_view": false
}
},
{ upsert: false, multi: true }
);
\ No newline at end of file
articles.find().forEach(art => {
art.images = art.images.map(img => "/assets/images/" + img);
articles.save(art);
});
\ No newline at end of file
......@@ -39,7 +39,7 @@ impl StaticFilesIndex {
fn _push_path(path: &Path, files: &mut Vec<String>, strip_from: &Path) {
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) {
......@@ -54,6 +54,10 @@ impl StaticFilesIndex {
let strip_from = StaticFilesIndex::get_public_dir(env);
StaticFilesIndex::_push_path(path, &mut self.0, &strip_from);
}
pub fn get_index(&self) -> Vec<String> {
self.0.clone()
}
}
#[derive(Debug, PartialEq)]
......
......@@ -95,6 +95,7 @@ async fn main() -> std::io::Result<()> {
.service(get_article)
.service(get_all_articles)
.service(post_files)
.service(get_static_files_index)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// STANDARD FILES ///////////////////////////////////////////////////////////////////////////////////////////
.service(resource("/favicon.ico").route(get().to(favicon)))
......
use crate::{core::static_files::*, middleware::AuthenticatedAdminMiddleware, AppState};
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 std::{
fs::{remove_file, File},
......@@ -106,6 +106,18 @@ pub async fn post_files(
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)
// use futures::TryStreamExt;
......
{
"name": "kuadrado-website",
"version": "1.0.4",
"version": "1.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "kuadrado-website",
"version": "1.0.4",
"version": "1.1.0",
"license": "MIT",
"dependencies": {
"ks-cheap-translator": "^0.1.0",
......
......@@ -4,7 +4,6 @@ const ImageCarousel = require("../generic-components/image-carousel");
const { getArticleBody } = require("../lib/article-utils");
const { fetch_json_or_error_text } = require("../lib/fetch");
const { MentaloEngine } = require("mentalo-engine");
const { images_url, data_url } = require("../../constants");
class GameArticle {
......@@ -41,16 +40,16 @@ class GameArticle {
tag: "button",
class: "play-button",
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;
button_element.innerHTML = "Loading ...";
button_element.style.pointerEvents = "none";
fetch_json_or_error_text(`${data_url}/${filename}`)
fetch_json_or_error_text(fileurl)
.then(game_data => {
const container = document.createElement("div");
container.style.position = "fixed";
......@@ -88,10 +87,10 @@ class GameArticle {
});
}
handle_click_play(filename, engine, e) {
handle_click_play(fileurl, engine, e) {
switch (engine) {
case "mentalo":
this.load_and_run_mentalo_game(filename, e.target);
this.load_and_run_mentalo_game(fileurl, e.target);
break;
default:
console.log("Error, unkown engine")
......@@ -116,7 +115,7 @@ class GameArticle {
tag: "div",
id: "article-banner",
style_rules: {
backgroundImage: `url(${images_url}/${images[0]})`,
backgroundImage: `url(${images[0]})`,
},
contents: [
{
......@@ -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 && {
tag: "div",
class: "article-details",
......
"use strict";
const ImageCarousel = require("../generic-components/image-carousel");
const { images_url } = require("../../constants");
const { getArticleBody } = require("../lib/article-utils");
......@@ -35,7 +34,7 @@ class SoftwareArticle {
contents: [
{
tag: "img",
src: `${images_url}/${logo}`
src: logo
},
]
},
......@@ -76,7 +75,7 @@ class SoftwareArticle {
tag: "div",
class: "article-more",
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 && {
tag: "div",
class: "article-details",
......
"use strict";
const { images_url } = require("../../constants");
class ThemeCard {
constructor(props) {
this.props = props;
......@@ -16,7 +14,12 @@ class ThemeCard {
{
tag: "div",
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",
......
......@@ -59,20 +59,20 @@ class HomePage extends WebPage {
contents: [
{
title: t("Jeux"),
img: "game_controller.svg",
img: images_url + "/game_controller.svg",
href: "/games/",
description:
t("games-description"),
},
{
title: t("Pédagogie"),
img: "brain.svg",
img: images_url + "/brain.svg",
href: "/education/",
description: t("education-description"),
},
{
title: "Software",
img: "meca_proc.svg",
img: images_url + "/meca_proc.svg",
href: "/software-development/",
description: t("software-description"),
},
......
"use strict";
const { images_url } = require("../../../../constants");
const ImageCarousel = require("../../../generic-components/image-carousel");
const { getArticleBody } = require("../../../lib/article-utils");
......@@ -27,7 +26,7 @@ class EduArticle {
tag: "div", class: "edu-art-image",
contents: [
{
tag: "img", src: `${images_url}/${images[0]}`
tag: "img", src: images[0]
}
]
},
......@@ -45,7 +44,7 @@ class EduArticle {
},
images.length > 1 && {
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 && {
......
"use strict";
const { images_url } = require("../../../../../admin-frontend/src/constants");
class GameThumb {
constructor(props) {
......@@ -21,7 +20,7 @@ class GameThumb {
target: "_blank",
class: "game-thumb",
style_rules: {
backgroundImage: `url(${images_url}/${images[0]})`,
backgroundImage: `url(${images[0]})`,
},
contents: [
{
......
"use strict";
const { images_url } = require("../../../../../admin-frontend/src/constants");
class SoftwareThumb {
constructor(props) {
......@@ -26,7 +25,7 @@ class SoftwareThumb {
class: "software-image",
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