As in they are not meant to work? Seriously, what do you mean? I don’t think users can do any of those things listed above, so clearly they do not “work as intended” - unless, of course, those are admin responsibilities.
I agree that you can change the Title field (as well as sort field, etc.), but the project record has more fields that could be of interest
But, I think this points to the confusion - when you enter a name in https://vassalengine.org/library/new you are presumably entering a project name, but the code underneath puts it as game name. And the project name is not the same as the project id (it is for those that were auto-generated). What exactly is supposed to happen?
Maybe https://vassalengine.org/library/new should just have a big button that says “Create New Project” and that will auto-assign a project name (a serial number, say), and then one can enter the meta information later on. But this begs the question as to how to navigate the projects - both as user and maintainer. Effectively, when users browse the “project” library at https://vassalengine.org/library/projects what they are seeing is not the projects but the games, and I can only the imagine the confusion when they see 10 versions of Napoleon at Waterloo.
Packages aren’t removable, image galleries aren’t editable, and there’s no link for creating packages because those things haven’t been implemented yet. If you found that you could do those things right now, that would be unexpected and cause for concern.
The project name is copied as the initial game title when you create a new project. You can edit the game title subsequently.
The project id isn’t something visible outside the database. The project name is what you’re seeing. For projects converted from the old module library, the project name happens to be the same as the proejct id.
If you want an example of how multiple projects for one game could look like when browsing/searching the library, have a look at the search results for Terraforming Mars; you will see the game name repeated 4 times, but under each is a different project name and a different project description so you can easily select the correct one.
Will they be though? Or will one be able to move releases or files between packages? Since the paradigm for the library has changed quite a bit, I can see many cases where that would be desirable.
Will they be?
It seems like @jrwatts was able to create projects though, or did some admin intervene?
I think it is fair to say that there are still many things missing form the new module library. Don’t get me wrong - I think there are many nice things about the new module library, but I don’t think it is really ready for “production” yet.
A suggestion: Perhaps one could add, above or below, the various input boxes, a short reminder of what is expected in that box. For example, when creating a new release, it could say
Release numbers are of the form x.y.z possibly with a patch version like x.y.z-a, where x,y,z are whole numbers, and a is alpha-numeric.
and for projects, it could say
Project names starts with a letter followed by up to 63 letters, digits, dashes, or underscores. Spaces, colons, periods, commas, and similar are not allowed.
to give the users a bit of help, and so admins are not constantly bugged with those issues.
What @jrwatts illustrates above I think shows the possible confusion. There’s no link between the various implementations of Terraforming Mars - other than the game name, and users could be excused for finding the choices hard to navigate. The lack of link between the projects also means that one Terraforming Mars project could say different things for - say - number of players, play time, and so on. The problem, I think, get’s compounded because the search does not only search for game names, but also looks elsewhere - e.g., a search for “Mars” or “Terraforming” shows a slew of projects.
I was able to create the 4 projects and then some packages within each, but Joel had to manually associate the files with the packages, because:
The files were already uploaded, so uploading them again would be a waste of time and (potentially) server space, and
Many of the existing module files do not comply to the new x.y.z versioning system, and so could not have been uploaded anyway.
I was able to set the Game Name, Player Count, Game Time, and sort name fields myself, however. If you look at the individual projects, you will also see that I edited the Readme sections to provide more info about which version did what and also added links to at least some of the other versions from the Readmes.
It would be fairly easy to implement removing empty packages or releases. I’ll probably do that soon.
Removing nonempty packages or releases is significantly more challenging, since the files in them would need to be moved somewhere else. It’s not clear to me how an interface for that should be, or if it should be possible at all, as I’d intended for file URLs to be permalinks.
Yes, but image galleries being editable is one of the lowest priority things on my list. All the code is pubic, so so someone could propose a PR to do it if waiting for me isn’t acceptable.
The error seems to originate from pub async fn project_post where the above "body" is (or perhaps full request) is wrapped in as JSON.
While I think you’ve made an effort to clarify the situation in the individual projects - which is the best that can be done, I think - it is far from built-in to the schema of the library. I would have preferred that the system did this for us, rather than relying on individuals to take the time and effort to do it.
But then moving a file to a different project/package/release should simply be to move the identifier to belong to the new project/package/release (change of one field in the DB). You could have a special project/package/release entry called Orphans where files are moved when their release is removed or similar, and then let the release interface import from there. The Orphans archive (and corresponding files) could then be periodically pruned to save server space.
BTW, what is the requires field for in the above record? I guess it is intended for extensions, saves, and logs.
With the caveat that I’m fairly new to Rust, I will venture a suggestion: Perhaps the "players" and "length" fields must be specified in "game"?
With the code (Updated to be more like the code in the backend)
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct MaybeRangePost {
pub min: Option<u32>,
pub max: Option<u32>
}
#[derive(Debug, thiserror::Error)]
#[error("min > max: {0:?}")]
pub struct RangePostError(MaybeRangePost);
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(try_from = "MaybeRangePost")]
pub struct RangePost {
pub min: Option<u32>,
pub max: Option<u32>
}
impl TryFrom<MaybeRangePost> for RangePost {
type Error = RangePostError;
fn try_from(m: MaybeRangePost) -> Result<Self, Self::Error> {
match (m.min, m.max) {
(None, _) | (_, None) => Ok(RangePost{ min: m.min, max: m.max }),
(Some(min), Some(max)) if min <= max =>
Ok(RangePost{ min: m.min, max: m.max }),
_ => Err(RangePostError(m))
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct GameDataPost {
pub title: String,
pub title_sort_key: String,
pub publisher: String,
pub year: String,
pub players: RangePost, // If Option<RangePost> then OK
pub length: RangePost // If Option<RangePost> then OK
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct MaybeProjectDataPost {
pub description: String,
pub tags: Vec<String>,
pub game: GameDataPost,
pub readme: String,
pub image: Option<String>
}
const DESCRIPTION_MAX_LENGTH: usize = 1024;
const GAME_TITLE_MAX_LENGTH: usize = 256;
const GAME_TITLE_SORT_KEY_MAX_LENGTH: usize = 256;
const GAME_PUBLISHER_MAX_LENGTH: usize = 256;
const GAME_YEAR_MAX_LENGTH: usize = 32;
const README_MAX_LENGTH: usize = 65536;
const IMAGE_MAX_LENGTH: usize = 256;
impl MaybeProjectDataPost {
fn overlong(&self) -> bool {
matches!(
&self.description,
s if s.len() > DESCRIPTION_MAX_LENGTH
) ||
matches!(
&self.readme,
s if s.len() > README_MAX_LENGTH
) ||
matches!(
&self.image,
Some(s) if s.len() > IMAGE_MAX_LENGTH
) ||
if let game = &self.game {
matches!(
&game.title,
s if s.len() > GAME_TITLE_MAX_LENGTH
) ||
matches!(
&game.title_sort_key,
s if s.len() > GAME_TITLE_SORT_KEY_MAX_LENGTH
) ||
matches!(
&game.publisher,
s if s.len() > GAME_PUBLISHER_MAX_LENGTH
) ||
matches!(
&game.year,
s if s.len() > GAME_YEAR_MAX_LENGTH
)
}
else {
false
}
}
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(try_from = "MaybeProjectDataPost")]
pub struct ProjectDataPost {
pub description: String,
pub tags: Vec<String>,
pub game: GameDataPost,
pub readme: String,
pub image: Option<String>
}
#[derive(Debug, thiserror::Error)]
#[error("invalid data {0:?}")]
pub struct ProjectDataPostError(MaybeProjectDataPost);
impl TryFrom<MaybeProjectDataPost> for ProjectDataPost {
type Error = ProjectDataPostError;
fn try_from(m: MaybeProjectDataPost) -> Result<Self, Self::Error> {
// field lengths must be within bounds
if m.overlong() {
Err(ProjectDataPostError(m))
}
else {
Ok(
ProjectDataPost{
description: m.description,
tags: m.tags,
game: m.game,
readme: m.readme,
image: m.image
}
)
}
}
}
fn untyped_example() -> serde_json::Result<ProjectDataPost> {
// Some JSON input data as a &str. Maybe this comes from the user.
let data_bad = r#"
{"description":"foo",
"tags":[],
"game":{
"title":"afrika_korps_wga",
"title_sort_key":"afrika_korps_wga",
"publisher":"",
"year":""},
"readme":""}"#;
let data_good = r#"
{"description":"foo",
"tags":[],
"game":{
"title":"afrika_korps_wga",
"title_sort_key":"afrika_korps_wga",
"publisher":"",
"year":"",
"players":{"min":0,"max":0},
"length":{"min":0,"max":0}},
"readme":""}"#;
let r: ProjectDataPost = serde_json::from_str(data_good)?;
println!("game is {:#?}",r);
let r : ProjectDataPost = serde_json::from_str(data_bad)?;
println!("game is {:#?}",r);
Ok(r)
}
fn main() {
let r = untyped_example();
match r {
Ok(data) => println!("{:#?}", data),
Err(error) => println!("{}", error)
};
}
for the data data_bad (with no “players” nor “length” fields from the “game” field), then I get a parse error. For data_good, which has those fields, I get no error. This is the output (Updated with respect to the above code)
Don’t see how to add sample images, but if I read rightly that is still in limbo. The big deal: adding the vmod file. I’ve got the package line, showing cube image, 1.0.0, then a +. I expected clicking on the plus to give me the upload capability, but if it does I haven’t figured it out. How do I associate a file with the package? That done, I presume I’m in.