use crate::{
error::{CanvasError, Result},
http::Requester,
pagination::PageStream,
params::wrap_params,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, Default, Clone, Serialize)]
pub struct UpdatePageParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub published: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub editing_roles: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub front_page: Option<bool>,
}
#[derive(Debug, Clone, Deserialize, Serialize, canvas_lms_api_derive::CanvasResource)]
pub struct Page {
pub page_id: Option<u64>,
pub url: Option<String>,
pub title: Option<String>,
pub created_at: Option<DateTime<Utc>>,
pub updated_at: Option<DateTime<Utc>>,
pub hide_from_students: Option<bool>,
pub editing_roles: Option<String>,
pub last_edited_by: Option<serde_json::Value>,
pub body: Option<String>,
pub published: Option<bool>,
pub front_page: Option<bool>,
pub locked_for_user: Option<bool>,
pub lock_info: Option<serde_json::Value>,
pub lock_explanation: Option<String>,
#[serde(skip)]
pub(crate) requester: Option<Arc<Requester>>,
#[serde(skip)]
pub course_id: Option<u64>,
#[serde(skip)]
pub group_id: Option<u64>,
}
impl Page {
fn parent_prefix(&self) -> Result<String> {
if let Some(id) = self.course_id {
Ok(format!("courses/{id}"))
} else if let Some(id) = self.group_id {
Ok(format!("groups/{id}"))
} else {
Err(CanvasError::BadRequest {
message: "Page does not have a course_id or group_id".to_string(),
errors: vec![],
})
}
}
fn url_slug(&self) -> &str {
self.url.as_deref().unwrap_or("")
}
fn propagate_context(&self, page: &mut Page) {
page.requester = self.requester.clone();
page.course_id = self.course_id;
page.group_id = self.group_id;
}
fn propagate_rev_context(&self, rev: &mut PageRevision) {
rev.requester = self.requester.clone();
rev.course_id = self.course_id;
rev.group_id = self.group_id;
}
pub async fn edit(&self, params: UpdatePageParams) -> Result<Page> {
let prefix = self.parent_prefix()?;
let form = wrap_params("wiki_page", ¶ms);
let mut page: Page = self
.req()
.put(&format!("{prefix}/pages/{}", self.url_slug()), &form)
.await?;
self.propagate_context(&mut page);
Ok(page)
}
pub async fn delete(&self) -> Result<Page> {
let prefix = self.parent_prefix()?;
let mut page: Page = self
.req()
.delete(&format!("{prefix}/pages/{}", self.url_slug()), &[])
.await?;
self.propagate_context(&mut page);
Ok(page)
}
pub fn get_revisions(&self) -> Result<PageStream<PageRevision>> {
let prefix = self.parent_prefix()?;
let slug = self.url_slug().to_string();
let course_id = self.course_id;
let group_id = self.group_id;
Ok(PageStream::new_with_injector(
Arc::clone(self.req()),
&format!("{prefix}/pages/{slug}/revisions"),
vec![],
move |mut r: PageRevision, req| {
r.requester = Some(Arc::clone(&req));
r.course_id = course_id;
r.group_id = group_id;
r
},
))
}
pub async fn get_revision_by_id(&self, revision_id: u64) -> Result<PageRevision> {
let prefix = self.parent_prefix()?;
let mut rev: PageRevision = self
.req()
.get(
&format!("{prefix}/pages/{}/revisions/{revision_id}", self.url_slug()),
&[],
)
.await?;
self.propagate_rev_context(&mut rev);
Ok(rev)
}
pub async fn show_latest_revision(&self) -> Result<PageRevision> {
let prefix = self.parent_prefix()?;
let mut rev: PageRevision = self
.req()
.get(
&format!("{prefix}/pages/{}/revisions/latest", self.url_slug()),
&[],
)
.await?;
self.propagate_rev_context(&mut rev);
Ok(rev)
}
pub async fn revert_to_revision(&self, revision_id: u64) -> Result<PageRevision> {
let prefix = self.parent_prefix()?;
let mut rev: PageRevision = self
.req()
.post(
&format!("{prefix}/pages/{}/revisions/{revision_id}", self.url_slug()),
&[],
)
.await?;
self.propagate_rev_context(&mut rev);
Ok(rev)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PageRevision {
pub revision_id: Option<u64>,
pub created_at: Option<DateTime<Utc>>,
pub updated_at: Option<DateTime<Utc>>,
pub latest: Option<bool>,
pub url: Option<String>,
pub title: Option<String>,
pub body: Option<String>,
pub edited_by: Option<serde_json::Value>,
#[serde(skip)]
pub(crate) requester: Option<Arc<Requester>>,
#[serde(skip)]
pub course_id: Option<u64>,
#[serde(skip)]
pub group_id: Option<u64>,
}