mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
Escape HTML entities if CSV is not HTML
Also use name 'is_html' consistently.
This commit is contained in:
parent
f7ac4497b4
commit
8f1bba6c07
6 changed files with 51 additions and 29 deletions
|
@ -111,7 +111,7 @@ message ImportCsvRequest {
|
|||
int64 notetype_id = 3;
|
||||
repeated CsvColumn columns = 4;
|
||||
uint32 delimiter = 5;
|
||||
bool allow_html = 6;
|
||||
bool is_html = 6;
|
||||
}
|
||||
|
||||
message CsvMetadataRequest {
|
||||
|
@ -125,5 +125,5 @@ message CsvMetadata {
|
|||
repeated string columns = 3;
|
||||
int64 deck_id = 4;
|
||||
int64 notetype_id = 5;
|
||||
optional bool html = 6;
|
||||
optional bool is_html = 6;
|
||||
}
|
||||
|
|
|
@ -416,7 +416,7 @@ class Collection(DeprecatedNamesMixin):
|
|||
notetype_id: NotetypeId,
|
||||
columns: list[CsvColumn],
|
||||
delimiter: int,
|
||||
allow_html: bool,
|
||||
is_html: bool,
|
||||
) -> ImportLogWithChanges:
|
||||
return self._backend.import_csv(
|
||||
path=path,
|
||||
|
@ -424,7 +424,7 @@ class Collection(DeprecatedNamesMixin):
|
|||
notetype_id=notetype_id,
|
||||
delimiter=delimiter,
|
||||
columns=columns,
|
||||
allow_html=allow_html,
|
||||
is_html=is_html,
|
||||
)
|
||||
|
||||
def import_json(self, json: str) -> ImportLogWithChanges:
|
||||
|
|
|
@ -100,7 +100,7 @@ class ImportDialog(QDialog):
|
|||
self.column_map = ColumnMap(self.columns, self.model)
|
||||
self._render_mapping()
|
||||
self._set_delimiter_button_text()
|
||||
self.frm.allowHTML.setChecked(self.html)
|
||||
self.frm.allowHTML.setChecked(self.is_html)
|
||||
self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get("importMode", 1))
|
||||
self.frm.tagModified.setText(self.tags)
|
||||
self.frm.tagModified.setCol(self.mw.col)
|
||||
|
@ -119,10 +119,10 @@ class ImportDialog(QDialog):
|
|||
else:
|
||||
self.model = self.mw.col.models.current()
|
||||
self.notetype_id = self.model["id"]
|
||||
if self.options.html is None:
|
||||
self.html = self.mw.pm.profile.get("allowHTML", True)
|
||||
if self.options.is_html is None:
|
||||
self.is_html = self.mw.pm.profile.get("allowHTML", True)
|
||||
else:
|
||||
self.html = self.options.html
|
||||
self.is_html = self.options.is_html
|
||||
|
||||
def _setup_choosers(self) -> None:
|
||||
import aqt.deckchooser
|
||||
|
@ -214,7 +214,7 @@ class ImportDialog(QDialog):
|
|||
notetype_id=self.model["id"],
|
||||
delimiter=self.delimiter,
|
||||
columns=self.column_map.csv_columns(),
|
||||
allow_html=self.frm.allowHTML.isChecked(),
|
||||
is_html=self.frm.allowHTML.isChecked(),
|
||||
),
|
||||
).with_backend_progress(import_progress_update).success(
|
||||
show_import_log
|
||||
|
|
|
@ -94,7 +94,7 @@ impl ImportExportService for Backend {
|
|||
input.notetype_id.into(),
|
||||
input.columns.into_iter().map(Into::into).collect(),
|
||||
try_into_byte(input.delimiter)?,
|
||||
//input.allow_html,
|
||||
input.is_html,
|
||||
)
|
||||
})
|
||||
.map(Into::into)
|
||||
|
|
|
@ -22,12 +22,12 @@ impl Collection {
|
|||
notetype_id: NotetypeId,
|
||||
columns: Vec<Column>,
|
||||
delimiter: u8,
|
||||
//allow_html: bool,
|
||||
is_html: bool,
|
||||
) -> Result<OpOutput<NoteLog>> {
|
||||
let notetype = self.get_notetype(notetype_id)?.ok_or(AnkiError::NotFound)?;
|
||||
let fields_len = notetype.fields.len();
|
||||
let file = File::open(path)?;
|
||||
let notes = deserialize_csv(file, &columns, fields_len, delimiter)?;
|
||||
let notes = deserialize_csv(file, &columns, fields_len, delimiter, is_html)?;
|
||||
|
||||
ForeignData {
|
||||
// TODO: refactor to allow passing ids directly
|
||||
|
@ -45,6 +45,7 @@ fn deserialize_csv(
|
|||
columns: &[Column],
|
||||
fields_len: usize,
|
||||
delimiter: u8,
|
||||
is_html: bool,
|
||||
) -> Result<Vec<ForeignNote>> {
|
||||
remove_tags_line_from_reader(&mut reader)?;
|
||||
let mut csv_reader = csv::ReaderBuilder::new()
|
||||
|
@ -54,7 +55,7 @@ fn deserialize_csv(
|
|||
.delimiter(delimiter)
|
||||
.trim(csv::Trim::All)
|
||||
.from_reader(reader);
|
||||
deserialize_csv_reader(&mut csv_reader, columns, fields_len)
|
||||
deserialize_csv_reader(&mut csv_reader, columns, fields_len, is_html)
|
||||
}
|
||||
|
||||
/// If the reader's first line starts with "tags:", which is allowed for historic
|
||||
|
@ -78,35 +79,45 @@ fn deserialize_csv_reader(
|
|||
reader: &mut csv::Reader<impl Read>,
|
||||
columns: &[Column],
|
||||
fields_len: usize,
|
||||
is_html: bool,
|
||||
) -> Result<Vec<ForeignNote>> {
|
||||
reader
|
||||
.records()
|
||||
.into_iter()
|
||||
.map(|res| {
|
||||
res.map(|record| ForeignNote::from_record(record, columns, fields_len))
|
||||
res.map(|record| ForeignNote::from_record(record, columns, fields_len, is_html))
|
||||
.map_err(Into::into)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl ForeignNote {
|
||||
fn from_record(record: csv::StringRecord, columns: &[Column], fields_len: usize) -> Self {
|
||||
fn from_record(
|
||||
record: csv::StringRecord,
|
||||
columns: &[Column],
|
||||
fields_len: usize,
|
||||
is_html: bool,
|
||||
) -> Self {
|
||||
let mut note = Self {
|
||||
fields: vec!["".to_string(); fields_len],
|
||||
..Default::default()
|
||||
};
|
||||
for (&column, value) in columns.iter().zip(record.iter()) {
|
||||
note.add_column_value(column, value);
|
||||
note.add_column_value(column, value, is_html);
|
||||
}
|
||||
note
|
||||
}
|
||||
|
||||
fn add_column_value(&mut self, column: Column, value: &str) {
|
||||
fn add_column_value(&mut self, column: Column, value: &str, is_html: bool) {
|
||||
match column {
|
||||
Column::Ignore => (),
|
||||
Column::Field(idx) => {
|
||||
if let Some(field) = self.fields.get_mut(idx) {
|
||||
field.push_str(value)
|
||||
*field = if is_html {
|
||||
value.to_string()
|
||||
} else {
|
||||
htmlescape::encode_minimal(value)
|
||||
};
|
||||
}
|
||||
}
|
||||
Column::Tags => self.tags.extend(value.split(' ').map(ToString::to_string)),
|
||||
|
@ -128,15 +139,11 @@ mod test {
|
|||
&$options.columns,
|
||||
$options.fields_len,
|
||||
$options.delimiter,
|
||||
$options.is_html,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(notes.len(), $expected.len());
|
||||
for (note, fields) in notes.iter().zip($expected.iter()) {
|
||||
assert_eq!(note.fields.len(), fields.len());
|
||||
for (note_field, field) in note.fields.iter().zip(fields.iter()) {
|
||||
assert_eq!(note_field, field);
|
||||
}
|
||||
}
|
||||
let fields: Vec<_> = notes.into_iter().map(|note| note.fields).collect();
|
||||
assert_eq!(fields, $expected);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -144,6 +151,7 @@ mod test {
|
|||
columns: Vec<Column>,
|
||||
fields_len: usize,
|
||||
delimiter: u8,
|
||||
is_html: bool,
|
||||
}
|
||||
|
||||
impl CsvOptions {
|
||||
|
@ -152,6 +160,7 @@ mod test {
|
|||
columns: vec![Column::Field(0), Column::Field(1)],
|
||||
fields_len: 2,
|
||||
delimiter: b',',
|
||||
is_html: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +183,12 @@ mod test {
|
|||
self.delimiter = delimiter;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_html(mut self, is_html: bool) -> Self {
|
||||
self.is_html = is_html;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -210,4 +225,11 @@ mod test {
|
|||
let options = CsvOptions::new();
|
||||
assert_imported_fields!(options, "#foo\nfront,back\n#bar\n", &[&["front", "back"]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_escape_html_entities_if_csv_is_html() {
|
||||
assert_imported_fields!(CsvOptions::new(), "<hr>\n", &[&["<hr>", ""]]);
|
||||
let with_html = CsvOptions::new().is_html(true);
|
||||
assert_imported_fields!(with_html, "<hr>\n", &[&["<hr>", ""]]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ impl Collection {
|
|||
metadata.notetype_id = nt.id.0;
|
||||
}
|
||||
}
|
||||
"html" => metadata.html = value.to_lowercase().parse::<bool>().ok(),
|
||||
"is_html" => metadata.is_html = value.to_lowercase().parse::<bool>().ok(),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
@ -245,9 +245,9 @@ mod test {
|
|||
#[test]
|
||||
fn should_detect_valid_html_toggle() {
|
||||
let mut col = open_test_collection();
|
||||
assert_eq!(metadata!(col, "#html:true\n").html, Some(true));
|
||||
assert_eq!(metadata!(col, "#html:FALSE\n").html, Some(false));
|
||||
assert_eq!(metadata!(col, "#html:maybe\n").html, None);
|
||||
assert_eq!(metadata!(col, "#is_html:true\n").is_html, Some(true));
|
||||
assert_eq!(metadata!(col, "#is_html:FALSE\n").is_html, Some(false));
|
||||
assert_eq!(metadata!(col, "#is_html:maybe\n").is_html, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue