diff --git a/__Rocket.toml__ b/__Rocket.toml__ deleted file mode 100644 index 90d4a85..0000000 --- a/__Rocket.toml__ +++ /dev/null @@ -1,20 +0,0 @@ -[default] -template_dir = "templates" -secret_key = "${ROCKET_SECRET_KEY}" -address = "0.0.0.0" -port = 8001 -ident = "Randerath Inventur" - -[default.tls] -certs="private/cert.pem" -key="private/key.pem" - -#[default.databases.inventur] -#url = "${ROCKET_DATABASE_URL}" - -[default.oauth.oauth] -auth_uri = "https://ldap.randerath.eu/realms/master/protocol/openid-connect/auth" -token_uri = "https://ldap.randerath.eu/realms/master/protocol/openid-connect/token" -client_id = "randy_inventur" -client_secret = "${ROCKET_CLIENT_SECRET}" -redirect_uri = "https://inventur.rander.at/auth" diff --git a/inventur_db/src/bin/add_column.rs b/inventur_db/src/bin/add_column.rs index 89ecddf..7666534 100644 --- a/inventur_db/src/bin/add_column.rs +++ b/inventur_db/src/bin/add_column.rs @@ -17,5 +17,5 @@ fn main() { .expect("Usage: add_column ."); let conn = &mut establish_connection(); - println!("{}", add_column(conn, table_id, name, uid).expect("Error")); + println!("{}", add_column(conn, table_id, name, crate::FIELDTYPE::TEXT, uid).expect("Error")); } diff --git a/inventur_db/src/bin/create_table.rs b/inventur_db/src/bin/create_table.rs index 13704f6..6f00563 100644 --- a/inventur_db/src/bin/create_table.rs +++ b/inventur_db/src/bin/create_table.rs @@ -34,5 +34,5 @@ fn main() { } - println!("{}", create_table(conn, name.to_string(), fields, uid).unwrap()); + println!("{}", create_table(conn, name.to_string(), fields.clone(), vec![crate::FIELDTYPE::TEXT; fields.len()], uid).unwrap()); } diff --git a/inventur_db/src/jrcells.rs b/inventur_db/src/jrcells.rs index 82dcfa5..d4d00d2 100644 --- a/inventur_db/src/jrcells.rs +++ b/inventur_db/src/jrcells.rs @@ -24,7 +24,7 @@ pub fn create_jrcell_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: i3 create_jrcell(conn, ntrid, clmid, &value) } -pub fn change_jrcell_value_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, idintbl: i32, new_value: String) -> Result { +pub fn change_jrcell_value_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, idintbl: i32, new_value: &String) -> Result { let ntrid = jrentries::get_jrentry_id(conn, tblid, rowpos); if ntrid.is_err() { return Err(ntrid.err().unwrap()); @@ -78,7 +78,7 @@ pub fn create_jrcell(conn: &mut MysqlConnection, entryid: i32, columnid: i32, va } -pub fn change_jrcell_value(conn: &mut MysqlConnection, entryid: i32, columnid: i32, new_value: String) -> Result { +pub fn change_jrcell_value(conn: &mut MysqlConnection, entryid: i32, columnid: i32, new_value: &String) -> Result { diesel::update(jrcells.filter(jrentry_id.eq(entryid)).filter(jrcolumn_id.eq(columnid))) .set(cell_value.eq(new_value)) .execute(conn) @@ -96,3 +96,10 @@ pub fn get_entry_cells(conn: &mut MysqlConnection, entryid: i32) -> Result Result, diesel::result::Error> { + jrcells + .filter(jrentry_id.eq(entryid)) + .select(id) + .load(conn) +} diff --git a/inventur_db/src/jrcolumns.rs b/inventur_db/src/jrcolumns.rs index 7f23ea3..aa5304b 100644 --- a/inventur_db/src/jrcolumns.rs +++ b/inventur_db/src/jrcolumns.rs @@ -7,7 +7,7 @@ use models::{ Jrcolumn, NewJrcolumn }; use schema::jrcolumns::dsl::{ jrcolumns, id, name, jrtable_id, id_in_table }; -pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String) -> Result { +pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String, clmtype: crate::FIELDTYPE) -> Result { use schema::jrentries::dsl::{ jrentries, jrtable_id }; use models::{ Jrentry, NewJrcell }; @@ -16,7 +16,7 @@ pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String) return ncols; } let ncols = ncols.unwrap() as i32; - let jrcolumn = NewJrcolumn { name: clmnname, jrtable_id: tblid, id_in_table: ncols + 1 }; + let jrcolumn = NewJrcolumn { name: clmnname.clone(), jrtable_id: tblid, column_type: clmtype as i32, id_in_table: (ncols + 1) as i32 }; let res = diesel::insert_into(crate::schema::jrcolumns::table) .values(&jrcolumn) .execute(conn); @@ -48,16 +48,11 @@ pub fn create_jrcolumn(conn: &mut MysqlConnection, tblid: i32, clmnname: String) } -pub fn create_jrcolumns_empty_tbl(conn: &mut MysqlConnection, tblid: i32, names: Vec) -> Result { +pub fn create_jrcolumns_empty_tbl(conn: &mut MysqlConnection, tblid: i32, names: Vec, types: Vec) -> Result { let mut cols : Vec = Vec::new(); - let ncols = jrtables::get_ncols(conn, tblid); - if ncols.is_err() { - return ncols; - } - let mut ncols = ncols.unwrap() as i32; - for clmnname in names { - ncols += 1; - cols.push(NewJrcolumn { name: clmnname, jrtable_id: tblid, id_in_table: ncols}); + + for i in 0..names.len() { + cols.push(NewJrcolumn { name: names[i].clone(), column_type: types[i] as i32, jrtable_id: tblid, id_in_table: (i+1) as i32}); } let cols = cols; @@ -169,8 +164,8 @@ pub fn move_column(conn: &mut MysqlConnection, clmnid: i32, new_id_in_table: i32 .execute(conn) } -pub fn delete_column_relative(conn: &mut MysqlConnection, tblid: i32, idinclm: i32) -> Result { - let clmid = get_clmid_relative(conn, tblid, idinclm); +pub fn delete_column_relative(conn: &mut MysqlConnection, tblid: i32, idintbl: i32) -> Result { + let clmid = get_clmid_relative(conn, tblid, idintbl); if clmid.is_err() { return Err(clmid.err().unwrap()); } diff --git a/inventur_db/src/jrentries.rs b/inventur_db/src/jrentries.rs index 0b0fd83..2226fd6 100644 --- a/inventur_db/src/jrentries.rs +++ b/inventur_db/src/jrentries.rs @@ -76,7 +76,6 @@ pub fn delete_jrentry_relative(conn: &mut MysqlConnection, tblid: i32, rowpos: return Err(entryid.err().unwrap()); } delete_jrentry(conn, entryid.unwrap()) - } pub fn delete_jrentry(conn: &mut MysqlConnection, entryid: i32) -> Result { diff --git a/inventur_db/src/jrtables.rs b/inventur_db/src/jrtables.rs index f9f3b70..ae8c892 100644 --- a/inventur_db/src/jrtables.rs +++ b/inventur_db/src/jrtables.rs @@ -6,7 +6,7 @@ use crate::jrcolumns; use models::{ Jrtable, NewJrtable }; use schema::jrtables::dsl::{ jrtables, id, name, owner_id }; -pub fn create_jrtable(conn: &mut MysqlConnection, tblname: &String, field_names: Vec, uid: i32) -> Result { +pub fn create_jrtable(conn: &mut MysqlConnection, tblname: &String, field_names: Vec, field_types: Vec, uid: i32) -> Result { let jrtable = NewJrtable { name: tblname.to_string(), owner_id: uid }; let tbl = diesel::insert_into(crate::schema::jrtables::table) @@ -24,7 +24,7 @@ pub fn create_jrtable(conn: &mut MysqlConnection, tblname: &String, field_names: return Err(tblid.err().unwrap()); } let tblid = tblid.unwrap(); - let cols = jrcolumns::create_jrcolumns_empty_tbl(conn, tblid, field_names); + let cols = jrcolumns::create_jrcolumns_empty_tbl(conn, tblid, field_names, field_types); if cols.is_err() { return Err(cols.err().unwrap()); } diff --git a/inventur_db/src/lib.rs b/inventur_db/src/lib.rs index 2ab8e34..127470a 100644 --- a/inventur_db/src/lib.rs +++ b/inventur_db/src/lib.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "512"] //! Database API using the table style system provided by this crate mod models; mod schema; @@ -17,6 +18,8 @@ use diesel::deserialize::FromSql; use diesel::deserialize; use diesel::backend::Backend; use diesel::sql_types::Integer; +use diesel::expression::Expression; + #[derive(PartialEq, Clone, Copy, diesel::FromSqlRow)] #[repr(i32)] @@ -25,6 +28,10 @@ pub enum FIELDTYPE { NUMBER = 1, } +impl Expression for FIELDTYPE { + type SqlType = Integer; +} + impl FromSql for FIELDTYPE where DB: Backend, i32: FromSql { fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { @@ -35,6 +42,15 @@ where DB: Backend, i32: FromSql { } } } + +impl From for FIELDTYPE { + fn from(value: isize) -> Self { + match value { + 1 => FIELDTYPE::NUMBER, + x => FIELDTYPE::TEXT, + } + } +} /// represents and summarised all relevant data of a table. /// Standard return type if whole tables should be returned @@ -169,7 +185,7 @@ pub fn search_table(tbl: Tbl, search_fields: Vec, search_value: String) -> let mut field_sets = HashSet::new(); for field in search_fields { for row in &rows { - if row.cells[field as usize].contains(&search_value) { + if row.cells[field as usize].to_lowercase().contains(&search_value) { field_sets.insert(row.clone()); } } @@ -241,11 +257,11 @@ pub fn delete_table(conn: &mut MysqlConnection, tblid: i32, uid: i32) -> Option< /// Returns the id of the newly created table if the action seems to have been successful and the requesting user is the /// table's owner. /// None otherwise. -pub fn create_table(conn: &mut MysqlConnection, name: String, field_names: Vec, uid: i32) -> Option { +pub fn create_table(conn: &mut MysqlConnection, name: String, field_names: Vec, field_types: Vec, uid: i32) -> Option { if jrtables::get_tbl_by_name_uid(conn, &name, uid).is_ok() { return None; } - if jrtables::create_jrtable(conn, &name, field_names, uid).is_err() { + if jrtables::create_jrtable(conn, &name, field_names, field_types, uid).is_err() { return None; } let tblid = jrtables::get_tbl_by_name_uid(conn, &name, uid); @@ -327,8 +343,21 @@ pub fn move_row(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, newpos: i32 Some(true) } +pub fn edit_row(conn: &mut MysqlConnection, tblid: i32, cells: Vec, row_pos: i32, uid: i32) -> Option { + let owner = jrtables::get_owner_id(conn, tblid); + if owner.is_err() || owner.unwrap() != uid { + return None; + } + for (i, cell) in cells.iter().enumerate() { + if jrcells::change_jrcell_value_relative(conn, tblid, row_pos, (i+1) as i32, cell).is_err() { + return None; + } + } + Some(true) +} -pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_pos: i32, new_value: String, uid: i32) -> Option { + +pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_pos: i32, new_value: &String, uid: i32) -> Option { let owner = jrtables::get_owner_id(conn, tblid); if owner.is_err() || owner.unwrap() != uid || @@ -350,11 +379,11 @@ pub fn move_column(conn: &mut MysqlConnection, tblid:i32, column_pos: i32, new_c Some(true) } -pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, uid: i32) -> Option { +pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, clmtype: FIELDTYPE, uid: i32) -> Option { let owner = jrtables::get_owner_id(conn, tblid); if owner.is_err() || owner.unwrap() != uid || - jrcolumns::create_jrcolumn(conn, tblid, name).is_err() + jrcolumns::create_jrcolumn(conn, tblid, name, clmtype).is_err() { return None; } diff --git a/inventur_db/src/models.rs b/inventur_db/src/models.rs index 7216264..e64ead6 100644 --- a/inventur_db/src/models.rs +++ b/inventur_db/src/models.rs @@ -37,6 +37,7 @@ pub struct Jrcolumn { pub struct NewJrcolumn { pub name: String, pub jrtable_id: i32, + pub column_type: i32, pub id_in_table: i32, } diff --git a/src/main.rs b/src/main.rs index ee60b6d..591a6ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,8 +65,9 @@ async fn rocket() -> _ { .attach(Db::fairing()) .attach(OAuth2::::fairing("oauth")) .mount("/", routes![auth::oauth_login, auth::oauth_callback, home, login_home]) - .mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create, table::import_table]) - .mount("/entry", routes![table::new_entry]) + .mount("/table", routes![table::table, table::table_sec, table::edit_tname, table::create, table::import_table, table::delete_table]) + .mount("/row", routes![table::new_entry, table::edit_entry, table::delete_entry]) + .mount("/column", routes![table::delete_column]) .register("/", catchers![auth::redirect_to_login]) .mount("/static", FileServer::from(relative!("static"))) diff --git a/src/table.rs b/src/table.rs index bcdba5a..1fe5ce6 100644 --- a/src/table.rs +++ b/src/table.rs @@ -9,8 +9,13 @@ use rocket::response::Redirect; use inventur_db; use rocket::serde::{Serialize, Deserialize, json::Json}; use rocket::http::{Status, Cookie, CookieJar, SameSite}; +use inventur_db::FIELDTYPE; - +#[derive(FromForm)] +struct DeleteColumn { + tblid: i32, + id_in_table: i32, +} #[derive(FromForm)] struct NewEntry { @@ -18,6 +23,19 @@ struct NewEntry { cells: Vec, } +#[derive(FromForm)] +struct EditEntry { + tblid: i32, + cells: Vec, + row_pos: i32, +} + +#[derive(FromForm)] +struct DeleteEntry { + tblid: i32, + row_pos: i32, +} + #[derive(FromForm)] struct EditTname { tblid: i32, @@ -27,7 +45,8 @@ struct EditTname { #[derive(FromForm)] struct NewTable { name: String, - fields: Vec, + field_names: Vec, + field_types: Vec, } #[derive(Deserialize)] @@ -38,6 +57,11 @@ struct ImportTable { rows: Vec>, } +#[derive(FromForm)] +struct DeleteTable { + tblid: i32, +} + #[derive(Serialize)] #[serde(crate = "rocket::serde")] struct JRows { @@ -109,7 +133,8 @@ pub async fn table(conn: Db, tid: i32, sort_dir: Option, sort_field: Option< let table = inventur_db::sort_table(table, sortfield, sortdir); let tname = table.name; - let columns = table.column_names; + let column_names = table.column_names; + let column_types = table.column_types.iter().map(|x: &inventur_db::FIELDTYPE| *x as i32).collect::>(); let rows : Vec>= table.rows.iter().map(|v| {let mut r = v.cells.clone(); r.insert(0, v.row_pos.to_string()); r}).collect(); let (tids, tnames) = get_tids(&conn, uid).await; @@ -124,13 +149,24 @@ pub async fn table(conn: Db, tid: i32, sort_dir: Option, sort_field: Option< tblname: tname, tids: tids, tnames: tnames, - columns: columns, + column_names: column_names, + column_types: column_types, rows: rows, } ) ) } +#[post("/delete", data="")] +pub async fn delete_table(conn: Db, data: Form, user: AuthUser) -> Result { + let uid = user.uid; + let tblid = data.tblid; + if conn.run(move |c| inventur_db::delete_table(c, tblid, uid)).await.is_none() { + return Err(Status::Forbidden); + } + Ok(Redirect::to(uri!("/"))) +} + #[post("/new", data="")] pub async fn new_entry(conn: Db, data: Form, user: AuthUser) -> Result { let uid = user.uid; @@ -142,6 +178,40 @@ pub async fn new_entry(conn: Db, data: Form, user: AuthUser) -> Result Ok(Redirect::to(uri!("/table", table_sec(tblid)))) } +#[post("/edit", data="")] +pub async fn edit_entry(conn: Db, data: Form, user: AuthUser) -> Result { + let uid = user.uid; + let cells = data.cells.clone(); + let tblid = data.tblid; + let row_pos = data.row_pos; + if conn.run(move |c| inventur_db::edit_row(c, tblid, cells, row_pos, uid)).await.is_none() { + return Err(Status::Forbidden); + } + Ok(Redirect::to(uri!("/table", table_sec(tblid)))) +} + +#[post("/delete", data="")] +pub async fn delete_entry(conn: Db, data: Form, user: AuthUser) -> Result { + let uid = user.uid; + let tblid = data.tblid; + let row_pos = data.row_pos; + if conn.run(move |c| inventur_db::delete_row(c, tblid, row_pos, uid)).await.is_none() { + return Err(Status::Forbidden); + } + Ok(Redirect::to(uri!("/table", table_sec(tblid)))) +} + +#[post("/delete", data="")] +pub async fn delete_column(conn: Db, data: Form, user: AuthUser) -> Result { + let uid = user.uid; + let tblid = data.tblid; + let idintbl = data.id_in_table; + if conn.run(move |c| inventur_db::delete_column(c, tblid, idintbl, uid)).await.is_none() { + return Err(Status::Forbidden); + } + Ok(Redirect::to(uri!("/table", table_sec(tblid)))) +} + #[post("/name/edit", data="")] pub async fn edit_tname(conn: Db, data: Form, user: AuthUser) -> Result { let uid = user.uid; @@ -155,7 +225,7 @@ pub async fn edit_tname(conn: Db, data: Form, user: AuthUser) -> Resu #[post("/create", data="")] pub async fn create(conn: Db, data: Form, user: AuthUser) -> Result { let uid = user.uid; - let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), data.fields.clone(), uid)).await; + let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), data.field_names.clone(), data.field_types.iter().map(|x: &isize| FIELDTYPE::from(*x)).collect::>(), uid)).await; if tblid.is_none() { return Err(Status::Forbidden); } @@ -167,7 +237,7 @@ pub async fn import_table(conn: Db, data: Json, user: AuthUser) -> let uid = user.uid; let columns = data.columns.clone(); let rows = data.rows.clone(); - let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), columns, uid)).await; + let tblid = conn.run(move |c| inventur_db::create_table(c, data.name.clone(), columns.clone(), vec![FIELDTYPE::TEXT; columns.len()], uid)).await; if tblid.is_none() { return Err(Status::UnprocessableEntity); } @@ -177,4 +247,3 @@ pub async fn import_table(conn: Db, data: Json, user: AuthUser) -> } Ok(Redirect::to(uri!("/table", table_sec(tblid)))) } - diff --git a/templates/base.html.tera b/templates/base.html.tera index 0d6043e..0b79471 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -1,193 +1,239 @@ {# vim: set filetype=html :#} + You should have received a copy of the GNU General Public License along with Inventory. If not, see . + --> - - - - - - - Inventory - {% block pagename %}{% endblock pagename %} - + Inventory - {% block pagename %}{% endblock pagename %} + - {% block script %}{% endblock script %} - + }; + } + }); + }; + function add_column() { + ncol += 1; + document.getElementById('dynamic_columns').innerHTML += ` +
+
+
+ ${ncol} +
+
+
+ +
+
+ +
+
`; + }; + function confirm_delete(form) { + document.getElementById(`form_edit_${form}`).reset(); + $('#confirm_delete_modal').modal('show'); + document.getElementById('confirm_delete_modal_entity').innerHTML = form; + document.getElementById('confirm_delete_modal_button').onclick = function () { + document.getElementById(`form_delete_${form}`).submit(); + } + } + + {% block script %}{% endblock script %} + + + - + {% block body %}{% endblock body %} -{% block body %}{% endblock body %} - - - -