Check ids when gathering data (#1928)
This will throw an error if a card, note or revlog id from the future is found during apkg import or export.
This commit is contained in:
parent
e6f158e445
commit
8478492190
@ -11,6 +11,7 @@ errors-multiple-notetypes-selected = Please select notes from only one notetype.
|
||||
errors-please-check-database = Please use the Check Database action, then try again.
|
||||
errors-please-check-media = Please use the Check Media action, then try again.
|
||||
errors-collection-too-new = This collection requires a newer version of Anki to open.
|
||||
errors-invalid-ids = This deck contains timestamps in the future. Please contact the deck author and ask them to fix the issue.
|
||||
|
||||
## Card Rendering
|
||||
|
||||
|
@ -39,6 +39,7 @@ impl AnkiError {
|
||||
AnkiError::ImportError(_) => Kind::ImportError,
|
||||
AnkiError::FileIoError(_) => Kind::IoError,
|
||||
AnkiError::MediaCheckRequired => Kind::InvalidInput,
|
||||
AnkiError::InvalidId => Kind::InvalidInput,
|
||||
};
|
||||
|
||||
pb::BackendError {
|
||||
|
@ -49,6 +49,7 @@ pub enum AnkiError {
|
||||
MediaCheckRequired,
|
||||
CustomStudyError(CustomStudyError),
|
||||
ImportError(ImportError),
|
||||
InvalidId,
|
||||
}
|
||||
|
||||
impl std::error::Error for AnkiError {}
|
||||
@ -105,6 +106,7 @@ impl AnkiError {
|
||||
AnkiError::CustomStudyError(err) => err.localized_description(tr),
|
||||
AnkiError::ImportError(err) => err.localized_description(tr),
|
||||
AnkiError::Deleted => tr.browsing_row_deleted().into(),
|
||||
AnkiError::InvalidId => tr.errors_invalid_ids().into(),
|
||||
AnkiError::IoError(_)
|
||||
| AnkiError::JsonError(_)
|
||||
| AnkiError::ProtoError(_)
|
||||
|
@ -41,6 +41,7 @@ impl ExchangeData {
|
||||
self.cards = col.gather_cards()?;
|
||||
self.decks = col.gather_decks()?;
|
||||
self.notetypes = col.gather_notetypes()?;
|
||||
self.check_ids()?;
|
||||
|
||||
if with_scheduling {
|
||||
self.revlog = col.gather_revlog()?;
|
||||
@ -114,6 +115,18 @@ impl ExchangeData {
|
||||
card.deck_id = deck_id;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ids(&self) -> Result<()> {
|
||||
let now = TimestampMillis::now().0;
|
||||
self.cards
|
||||
.iter()
|
||||
.map(|card| card.id.0)
|
||||
.chain(self.notes.iter().map(|note| note.id.0))
|
||||
.chain(self.revlog.iter().map(|entry| entry.id.0))
|
||||
.any(|timestamp| timestamp > now)
|
||||
.then(|| Err(AnkiError::InvalidId))
|
||||
.unwrap_or(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_media_names_from_note(
|
||||
@ -225,3 +238,36 @@ impl Collection {
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{collection::open_test_collection, search::SearchNode};
|
||||
|
||||
#[test]
|
||||
fn should_gather_valid_notes() {
|
||||
let mut data = ExchangeData::default();
|
||||
let mut col = open_test_collection();
|
||||
|
||||
let note = col.add_new_note("Basic");
|
||||
data.gather_data(&mut col, SearchNode::WholeCollection, true)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(data.notes, [note]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_err_if_note_has_invalid_id() {
|
||||
let mut data = ExchangeData::default();
|
||||
let mut col = open_test_collection();
|
||||
let now_micros = TimestampMillis::now().0 * 1000;
|
||||
|
||||
let mut note = col.add_new_note("Basic");
|
||||
note.id = NoteId(now_micros);
|
||||
col.add_note_only_with_id_undoable(&mut note).unwrap();
|
||||
|
||||
assert!(data
|
||||
.gather_data(&mut col, SearchNode::WholeCollection, true)
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user