lib.rs 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542
  1. // Ensure we're `no_std` when compiling for Wasm.
  2. #![cfg_attr(not(feature = "std"), no_std)]
  3. #![recursion_limit = "256"]
  4. // Internal Substrate warning (decl_event).
  5. #![allow(clippy::unused_unit, clippy::all)]
  6. #[cfg(test)]
  7. mod tests;
  8. mod errors;
  9. mod permissions;
  10. pub use errors::*;
  11. pub use permissions::*;
  12. use core::hash::Hash;
  13. use codec::Codec;
  14. use codec::{Decode, Encode};
  15. pub use storage::{
  16. BagIdType, DataObjectCreationParameters, DataObjectStorage, DynamicBagIdType, UploadParameters,
  17. UploadParametersRecord,
  18. };
  19. use frame_support::{
  20. decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure, traits::Get, Parameter,
  21. };
  22. use frame_system::ensure_signed;
  23. #[cfg(feature = "std")]
  24. pub use serde::{Deserialize, Serialize};
  25. use sp_arithmetic::traits::{BaseArithmetic, One, Zero};
  26. use sp_runtime::traits::{MaybeSerializeDeserialize, Member};
  27. use sp_std::collections::btree_set::BTreeSet;
  28. use sp_std::vec::Vec;
  29. pub use common::{
  30. currency::{BalanceOf, GovernanceCurrency},
  31. working_group::WorkingGroup,
  32. AssetUrls, MembershipTypes, StorageOwnership,
  33. };
  34. type Storage<T> = storage::Module<T>;
  35. /// Type, used in diffrent numeric constraints representations
  36. pub type MaxNumber = u32;
  37. /// A numeric identifier trait
  38. pub trait NumericIdentifier:
  39. Parameter
  40. + Member
  41. + BaseArithmetic
  42. + Codec
  43. + Default
  44. + Copy
  45. + Clone
  46. + Hash
  47. + MaybeSerializeDeserialize
  48. + Eq
  49. + PartialEq
  50. + Ord
  51. + Zero
  52. {
  53. }
  54. impl NumericIdentifier for u64 {}
  55. /// Module configuration trait for Content Directory Module
  56. pub trait Trait:
  57. frame_system::Trait + ContentActorAuthenticator + Clone + GovernanceCurrency + storage::Trait
  58. {
  59. /// The overarching event type.
  60. type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
  61. /// Channel Transfer Payments Escrow Account seed for ModuleId to compute deterministic AccountId
  62. type ChannelOwnershipPaymentEscrowId: Get<[u8; 8]>;
  63. /// Type of identifier for Videos
  64. type VideoId: NumericIdentifier;
  65. /// Type of identifier for Video Categories
  66. type VideoCategoryId: NumericIdentifier;
  67. /// Type of identifier for Channel Categories
  68. type ChannelCategoryId: NumericIdentifier;
  69. /// Type of identifier for Playlists
  70. type PlaylistId: NumericIdentifier;
  71. /// Type of identifier for Persons
  72. type PersonId: NumericIdentifier;
  73. /// Type of identifier for Channels
  74. type SeriesId: NumericIdentifier;
  75. /// Type of identifier for Channel transfer requests
  76. type ChannelOwnershipTransferRequestId: NumericIdentifier;
  77. /// The maximum number of curators per group constraint
  78. type MaxNumberOfCuratorsPerGroup: Get<MaxNumber>;
  79. /// The storage type used
  80. type DataObjectStorage: storage::DataObjectStorage<Self>;
  81. }
  82. /// The owner of a channel, is the authorized "actor" that can update
  83. /// or delete or transfer a channel and its contents.
  84. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  85. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  86. pub enum ChannelOwner<MemberId, CuratorGroupId> {
  87. /// A Member owns the channel
  88. Member(MemberId),
  89. /// A specific curation group owns the channel
  90. CuratorGroup(CuratorGroupId),
  91. }
  92. // simplification type
  93. pub(crate) type ActorToChannelOwnerResult<T> = Result<
  94. ChannelOwner<
  95. <T as common::MembershipTypes>::MemberId,
  96. <T as ContentActorAuthenticator>::CuratorGroupId,
  97. >,
  98. Error<T>,
  99. >;
  100. // Default trait implemented only because its used in a Channel which needs to implement a Default trait
  101. // since it is a StorageValue.
  102. impl<MemberId: Default, CuratorGroupId> Default for ChannelOwner<MemberId, CuratorGroupId> {
  103. fn default() -> Self {
  104. ChannelOwner::Member(MemberId::default())
  105. }
  106. }
  107. /// A category which channels can belong to.
  108. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  109. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  110. pub struct ChannelCategory {
  111. // No runtime information is currently stored for a Category.
  112. }
  113. /// Information on the category being created.
  114. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  115. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  116. pub struct ChannelCategoryCreationParameters {
  117. /// Metadata for the category.
  118. meta: Vec<u8>,
  119. }
  120. /// Information on the category being updated.
  121. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  122. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  123. pub struct ChannelCategoryUpdateParameters {
  124. // as this is the only field it is not an Option
  125. /// Metadata update for the category.
  126. new_meta: Vec<u8>,
  127. }
  128. /// Type representing an owned channel which videos, playlists, and series can belong to.
  129. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  130. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  131. pub struct ChannelRecord<MemberId, CuratorGroupId, AccountId> {
  132. /// The owner of a channel
  133. owner: ChannelOwner<MemberId, CuratorGroupId>,
  134. /// The videos under this channel
  135. num_videos: u64,
  136. /// If curators have censored this channel or not
  137. is_censored: bool,
  138. /// Reward account where revenue is sent if set.
  139. reward_account: Option<AccountId>,
  140. /// Account for withdrawing deletion prize funds
  141. deletion_prize_source_account_id: AccountId,
  142. }
  143. // Channel alias type for simplification.
  144. pub type Channel<T> = ChannelRecord<
  145. <T as common::MembershipTypes>::MemberId,
  146. <T as ContentActorAuthenticator>::CuratorGroupId,
  147. <T as frame_system::Trait>::AccountId,
  148. >;
  149. /// A request to buy a channel by a new ChannelOwner.
  150. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  151. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  152. pub struct ChannelOwnershipTransferRequestRecord<
  153. ChannelId,
  154. MemberId,
  155. CuratorGroupId,
  156. Balance,
  157. AccountId,
  158. > {
  159. channel_id: ChannelId,
  160. new_owner: ChannelOwner<MemberId, CuratorGroupId>,
  161. payment: Balance,
  162. new_reward_account: Option<AccountId>,
  163. }
  164. // ChannelOwnershipTransferRequest type alias for simplification.
  165. pub type ChannelOwnershipTransferRequest<T> = ChannelOwnershipTransferRequestRecord<
  166. <T as storage::Trait>::ChannelId,
  167. <T as common::MembershipTypes>::MemberId,
  168. <T as ContentActorAuthenticator>::CuratorGroupId,
  169. BalanceOf<T>,
  170. <T as frame_system::Trait>::AccountId,
  171. >;
  172. /// Information about channel being created.
  173. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  174. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  175. pub struct ChannelCreationParametersRecord<StorageAssets, AccountId> {
  176. /// Asset collection for the channel, referenced by metadata
  177. assets: Option<StorageAssets>,
  178. /// Metadata about the channel.
  179. meta: Option<Vec<u8>>,
  180. /// optional reward account
  181. reward_account: Option<AccountId>,
  182. }
  183. type ChannelCreationParameters<T> =
  184. ChannelCreationParametersRecord<StorageAssets<T>, <T as frame_system::Trait>::AccountId>;
  185. /// Information about channel being updated.
  186. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  187. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  188. pub struct ChannelUpdateParametersRecord<StorageAssets, AccountId, DataObjectId: Ord> {
  189. /// Asset collection for the channel, referenced by metadata
  190. assets_to_upload: Option<StorageAssets>,
  191. /// If set, metadata update for the channel.
  192. new_meta: Option<Vec<u8>>,
  193. /// If set, updates the reward account of the channel
  194. reward_account: Option<Option<AccountId>>,
  195. /// assets to be removed from channel
  196. assets_to_remove: BTreeSet<DataObjectId>,
  197. }
  198. type ChannelUpdateParameters<T> = ChannelUpdateParametersRecord<
  199. StorageAssets<T>,
  200. <T as frame_system::Trait>::AccountId,
  201. DataObjectId<T>,
  202. >;
  203. /// A category that videos can belong to.
  204. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  205. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  206. pub struct VideoCategory {
  207. // No runtime information is currently stored for a Category.
  208. }
  209. /// Information about the video category being created.
  210. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  211. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  212. pub struct VideoCategoryCreationParameters {
  213. /// Metadata about the video category.
  214. meta: Vec<u8>,
  215. }
  216. /// Information about the video category being updated.
  217. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  218. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  219. pub struct VideoCategoryUpdateParameters {
  220. // Because it is the only field it is not an Option
  221. /// Metadata update for the video category.
  222. new_meta: Vec<u8>,
  223. }
  224. /// Information regarding the content being uploaded
  225. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  226. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  227. pub struct StorageAssetsRecord<Balance> {
  228. /// Data object parameters.
  229. pub object_creation_list: Vec<DataObjectCreationParameters>,
  230. /// Expected data size fee value for this extrinsic call.
  231. pub expected_data_size_fee: Balance,
  232. }
  233. type StorageAssets<T> = StorageAssetsRecord<<T as balances::Trait>::Balance>;
  234. /// Information about the video being created.
  235. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  236. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  237. pub struct VideoCreationParametersRecord<StorageAssets> {
  238. /// Asset collection for the video
  239. assets: Option<StorageAssets>,
  240. /// Metadata for the video.
  241. meta: Option<Vec<u8>>,
  242. }
  243. type VideoCreationParameters<T> = VideoCreationParametersRecord<StorageAssets<T>>;
  244. /// Information about the video being updated
  245. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  246. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  247. pub struct VideoUpdateParametersRecord<StorageAssets, DataObjectId: Ord> {
  248. /// Assets referenced by metadata
  249. assets_to_upload: Option<StorageAssets>,
  250. /// If set, metadata update for the video.
  251. new_meta: Option<Vec<u8>>,
  252. /// video assets to be removed from channel
  253. assets_to_remove: BTreeSet<DataObjectId>,
  254. }
  255. type VideoUpdateParameters<T> = VideoUpdateParametersRecord<StorageAssets<T>, DataObjectId<T>>;
  256. /// A video which belongs to a channel. A video may be part of a series or playlist.
  257. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  258. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  259. pub struct VideoRecord<ChannelId, SeriesId> {
  260. pub in_channel: ChannelId,
  261. // keep track of which season the video is in if it is an 'episode'
  262. // - prevent removing a video if it is in a season (because order is important)
  263. pub in_series: Option<SeriesId>,
  264. /// Whether the curators have censored the video or not.
  265. pub is_censored: bool,
  266. }
  267. type Video<T> = VideoRecord<<T as storage::Trait>::ChannelId, <T as Trait>::SeriesId>;
  268. /// Information about the plyalist being created.
  269. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  270. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  271. pub struct PlaylistCreationParameters {
  272. /// Metadata about the playlist.
  273. meta: Vec<u8>,
  274. }
  275. /// Information about the playlist being updated.
  276. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  277. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  278. pub struct PlaylistUpdateParameters {
  279. // It is the only field so its not an Option
  280. /// Metadata update for the playlist.
  281. new_meta: Vec<u8>,
  282. }
  283. /// A playlist is an ordered collection of videos.
  284. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  285. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  286. pub struct Playlist<ChannelId> {
  287. /// The channel the playlist belongs to.
  288. in_channel: ChannelId,
  289. }
  290. /// Information about the episode being created or updated.
  291. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  292. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  293. pub enum EpisodeParameters<VideoId, StorageAssets> {
  294. /// A new video is being added as the episode.
  295. NewVideo(VideoCreationParametersRecord<StorageAssets>),
  296. /// An existing video is being made into an episode.
  297. ExistingVideo(VideoId),
  298. }
  299. /// Information about the season being created or updated.
  300. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  301. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  302. pub struct SeasonParameters<VideoId, StorageAssets> {
  303. /// Season assets referenced by metadata
  304. assets: Option<StorageAssets>,
  305. // ?? It might just be more straighforward to always provide full list of episodes at cost of larger tx.
  306. /// If set, updates the episodes of a season. Extends the number of episodes in a season
  307. /// when length of new_episodes is greater than previously set. Last elements must all be
  308. /// 'Some' in that case.
  309. /// Will truncate existing season when length of new_episodes is less than previously set.
  310. episodes: Option<Vec<Option<EpisodeParameters<VideoId, StorageAssets>>>>,
  311. meta: Option<Vec<u8>>,
  312. }
  313. /// Information about the series being created or updated.
  314. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  315. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  316. pub struct SeriesParameters<VideoId, StorageAssets> {
  317. /// Series assets referenced by metadata
  318. assets: Option<StorageAssets>,
  319. // ?? It might just be more straighforward to always provide full list of seasons at cost of larger tx.
  320. /// If set, updates the seasons of a series. Extend a series when length of seasons is
  321. /// greater than previoulsy set. Last elements must all be 'Some' in that case.
  322. /// Will truncate existing series when length of seasons is less than previously set.
  323. seasons: Option<Vec<Option<SeasonParameters<VideoId, StorageAssets>>>>,
  324. meta: Option<Vec<u8>>,
  325. }
  326. /// A season is an ordered list of videos (episodes).
  327. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  328. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  329. pub struct Season<VideoId> {
  330. episodes: Vec<VideoId>,
  331. }
  332. /// A series is an ordered list of seasons that belongs to a channel.
  333. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  334. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  335. pub struct Series<ChannelId, VideoId> {
  336. in_channel: ChannelId,
  337. seasons: Vec<Season<VideoId>>,
  338. }
  339. /// The actor the caller/origin is trying to act as for Person creation and update and delete calls.
  340. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  341. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  342. pub enum PersonActor<MemberId, CuratorId> {
  343. Member(MemberId),
  344. Curator(CuratorId),
  345. }
  346. /// The authorized actor that may update or delete a Person.
  347. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  348. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  349. pub enum PersonController<MemberId> {
  350. /// Member controls the person
  351. Member(MemberId),
  352. /// Any curator controls the person
  353. Curators,
  354. }
  355. /// Default trait implemented only because its used in Person which needs to implement a Default trait
  356. /// since it is a StorageValue.
  357. impl<MemberId: Default> Default for PersonController<MemberId> {
  358. fn default() -> Self {
  359. PersonController::Member(MemberId::default())
  360. }
  361. }
  362. /// Information for Person being created.
  363. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  364. #[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
  365. pub struct PersonCreationParameters<StorageAssets> {
  366. /// Assets referenced by metadata
  367. assets: StorageAssets,
  368. /// Metadata for person.
  369. meta: Vec<u8>,
  370. }
  371. /// Information for Persion being updated.
  372. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  373. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  374. pub struct PersonUpdateParameters<StorageAssets> {
  375. /// Assets referenced by metadata
  376. assets: Option<StorageAssets>,
  377. /// Metadata to update person.
  378. new_meta: Option<Vec<u8>>,
  379. }
  380. /// A Person represents a real person that may be associated with a video.
  381. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
  382. #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)]
  383. pub struct Person<MemberId> {
  384. /// Who can update or delete this person.
  385. controlled_by: PersonController<MemberId>,
  386. }
  387. type DataObjectId<T> = <T as storage::Trait>::DataObjectId;
  388. decl_storage! {
  389. trait Store for Module<T: Trait> as Content {
  390. pub ChannelById get(fn channel_by_id): map hasher(blake2_128_concat) T::ChannelId => Channel<T>;
  391. pub ChannelCategoryById get(fn channel_category_by_id): map hasher(blake2_128_concat) T::ChannelCategoryId => ChannelCategory;
  392. pub VideoById get(fn video_by_id): map hasher(blake2_128_concat) T::VideoId => Video<T>;
  393. pub VideoCategoryById get(fn video_category_by_id): map hasher(blake2_128_concat) T::VideoCategoryId => VideoCategory;
  394. pub PlaylistById get(fn playlist_by_id): map hasher(blake2_128_concat) T::PlaylistId => Playlist<T::ChannelId>;
  395. pub SeriesById get(fn series_by_id): map hasher(blake2_128_concat) T::SeriesId => Series<T::ChannelId, T::VideoId>;
  396. pub PersonById get(fn person_by_id): map hasher(blake2_128_concat) T::PersonId => Person<T::MemberId>;
  397. pub ChannelOwnershipTransferRequestById get(fn channel_ownership_transfer_request_by_id):
  398. map hasher(blake2_128_concat) T::ChannelOwnershipTransferRequestId => ChannelOwnershipTransferRequest<T>;
  399. pub NextChannelCategoryId get(fn next_channel_category_id) config(): T::ChannelCategoryId;
  400. pub NextChannelId get(fn next_channel_id) config(): T::ChannelId;
  401. pub NextVideoCategoryId get(fn next_video_category_id) config(): T::VideoCategoryId;
  402. pub NextVideoId get(fn next_video_id) config(): T::VideoId;
  403. pub NextPlaylistId get(fn next_playlist_id) config(): T::PlaylistId;
  404. pub NextPersonId get(fn next_person_id) config(): T::PersonId;
  405. pub NextSeriesId get(fn next_series_id) config(): T::SeriesId;
  406. pub NextChannelOwnershipTransferRequestId get(fn next_channel_transfer_request_id) config(): T::ChannelOwnershipTransferRequestId;
  407. pub NextCuratorGroupId get(fn next_curator_group_id) config(): T::CuratorGroupId;
  408. /// Map, representing CuratorGroupId -> CuratorGroup relation
  409. pub CuratorGroupById get(fn curator_group_by_id): map hasher(blake2_128_concat) T::CuratorGroupId => CuratorGroup<T>;
  410. }
  411. }
  412. decl_module! {
  413. pub struct Module<T: Trait> for enum Call where origin: T::Origin {
  414. /// Predefined errors
  415. type Error = Error<T>;
  416. /// Initializing events
  417. fn deposit_event() = default;
  418. /// Exports const - max number of curators per group
  419. const MaxNumberOfCuratorsPerGroup: MaxNumber = T::MaxNumberOfCuratorsPerGroup::get();
  420. // ======
  421. // Next set of extrinsics can only be invoked by lead.
  422. // ======
  423. /// Add new curator group to runtime storage
  424. #[weight = 10_000_000] // TODO: adjust weight
  425. pub fn create_curator_group(
  426. origin,
  427. ) {
  428. // Ensure given origin is lead
  429. ensure_is_lead::<T>(origin)?;
  430. //
  431. // == MUTATION SAFE ==
  432. //
  433. let curator_group_id = Self::next_curator_group_id();
  434. // Insert empty curator group with `active` parameter set to false
  435. <CuratorGroupById<T>>::insert(curator_group_id, CuratorGroup::<T>::default());
  436. // Increment the next curator curator_group_id:
  437. <NextCuratorGroupId<T>>::mutate(|n| *n += T::CuratorGroupId::one());
  438. // Trigger event
  439. Self::deposit_event(RawEvent::CuratorGroupCreated(curator_group_id));
  440. }
  441. /// Set `is_active` status for curator group under given `curator_group_id`
  442. #[weight = 10_000_000] // TODO: adjust weight
  443. pub fn set_curator_group_status(
  444. origin,
  445. curator_group_id: T::CuratorGroupId,
  446. is_active: bool,
  447. ) {
  448. // Ensure given origin is lead
  449. ensure_is_lead::<T>(origin)?;
  450. // Ensure curator group under provided curator_group_id already exist
  451. Self::ensure_curator_group_under_given_id_exists(&curator_group_id)?;
  452. //
  453. // == MUTATION SAFE ==
  454. //
  455. // Set `is_active` status for curator group under given `curator_group_id`
  456. <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
  457. curator_group.set_status(is_active)
  458. });
  459. // Trigger event
  460. Self::deposit_event(RawEvent::CuratorGroupStatusSet(curator_group_id, is_active));
  461. }
  462. /// Add curator to curator group under given `curator_group_id`
  463. #[weight = 10_000_000] // TODO: adjust weight
  464. pub fn add_curator_to_group(
  465. origin,
  466. curator_group_id: T::CuratorGroupId,
  467. curator_id: T::CuratorId,
  468. ) {
  469. // Ensure given origin is lead
  470. ensure_is_lead::<T>(origin)?;
  471. // Ensure curator group under provided curator_group_id already exist, retrieve corresponding one
  472. let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
  473. // Ensure that curator_id is infact a worker in content working group
  474. ensure_is_valid_curator_id::<T>(&curator_id)?;
  475. // Ensure max number of curators per group limit not reached yet
  476. curator_group.ensure_max_number_of_curators_limit_not_reached()?;
  477. // Ensure curator under provided curator_id isn`t a CuratorGroup member yet
  478. curator_group.ensure_curator_in_group_does_not_exist(&curator_id)?;
  479. //
  480. // == MUTATION SAFE ==
  481. //
  482. // Insert curator_id into curator_group under given curator_group_id
  483. <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
  484. curator_group.get_curators_mut().insert(curator_id);
  485. });
  486. // Trigger event
  487. Self::deposit_event(RawEvent::CuratorAdded(curator_group_id, curator_id));
  488. }
  489. /// Remove curator from a given curator group
  490. #[weight = 10_000_000] // TODO: adjust weight
  491. pub fn remove_curator_from_group(
  492. origin,
  493. curator_group_id: T::CuratorGroupId,
  494. curator_id: T::CuratorId,
  495. ) {
  496. // Ensure given origin is lead
  497. ensure_is_lead::<T>(origin)?;
  498. // Ensure curator group under provided curator_group_id already exist, retrieve corresponding one
  499. let curator_group = Self::ensure_curator_group_exists(&curator_group_id)?;
  500. // Ensure curator under provided curator_id is CuratorGroup member
  501. curator_group.ensure_curator_in_group_exists(&curator_id)?;
  502. //
  503. // == MUTATION SAFE ==
  504. //
  505. // Remove curator_id from curator_group under given curator_group_id
  506. <CuratorGroupById<T>>::mutate(curator_group_id, |curator_group| {
  507. curator_group.get_curators_mut().remove(&curator_id);
  508. });
  509. // Trigger event
  510. Self::deposit_event(RawEvent::CuratorRemoved(curator_group_id, curator_id));
  511. }
  512. // TODO: Add Option<reward_account> to ChannelCreationParameters ?
  513. #[weight = 10_000_000] // TODO: adjust weight
  514. pub fn create_channel(
  515. origin,
  516. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  517. params: ChannelCreationParameters<T>,
  518. ) {
  519. ensure_actor_authorized_to_create_channel::<T>(
  520. origin.clone(),
  521. &actor,
  522. )?;
  523. // channel creator account
  524. let sender = ensure_signed(origin)?;
  525. // The channel owner will be..
  526. let channel_owner = Self::actor_to_channel_owner(&actor)?;
  527. // next channel id
  528. let channel_id = NextChannelId::<T>::get();
  529. // atomically upload to storage and return the # of uploaded assets
  530. if let Some(upload_assets) = params.assets.as_ref() {
  531. Self::upload_assets_to_storage(
  532. upload_assets,
  533. &channel_id,
  534. &sender,
  535. )?;
  536. }
  537. //
  538. // == MUTATION SAFE ==
  539. //
  540. // Only increment next channel id if adding content was successful
  541. NextChannelId::<T>::mutate(|id| *id += T::ChannelId::one());
  542. // channel creation
  543. let channel: Channel<T> = ChannelRecord {
  544. owner: channel_owner,
  545. // a newly create channel has zero videos ??
  546. num_videos: 0u64,
  547. is_censored: false,
  548. reward_account: params.reward_account.clone(),
  549. // setting the channel owner account as the prize funds account
  550. deletion_prize_source_account_id: sender,
  551. };
  552. // add channel to onchain state
  553. ChannelById::<T>::insert(channel_id, channel.clone());
  554. Self::deposit_event(RawEvent::ChannelCreated(actor, channel_id, channel, params));
  555. }
  556. // Include Option<AccountId> in ChannelUpdateParameters to update reward_account
  557. #[weight = 10_000_000] // TODO: adjust weight
  558. pub fn update_channel(
  559. origin,
  560. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  561. channel_id: T::ChannelId,
  562. params: ChannelUpdateParameters<T>,
  563. ) {
  564. // check that channel exists
  565. let channel = Self::ensure_channel_exists(&channel_id)?;
  566. ensure_actor_authorized_to_update_channel::<T>(
  567. origin,
  568. &actor,
  569. &channel.owner,
  570. )?;
  571. Self::remove_assets_from_storage(&params.assets_to_remove, &channel_id, &channel.deletion_prize_source_account_id)?;
  572. // atomically upload to storage and return the # of uploaded assets
  573. if let Some(upload_assets) = params.assets_to_upload.as_ref() {
  574. Self::upload_assets_to_storage(
  575. upload_assets,
  576. &channel_id,
  577. &channel.deletion_prize_source_account_id
  578. )?;
  579. }
  580. //
  581. // == MUTATION SAFE ==
  582. //
  583. let mut channel = channel;
  584. // Maybe update the reward account
  585. if let Some(reward_account) = &params.reward_account {
  586. channel.reward_account = reward_account.clone();
  587. }
  588. // Update the channel
  589. ChannelById::<T>::insert(channel_id, channel.clone());
  590. Self::deposit_event(RawEvent::ChannelUpdated(actor, channel_id, channel, params));
  591. }
  592. // extrinsics for channel deletion
  593. #[weight = 10_000_000] // TODO: adjust weight
  594. pub fn delete_channel(
  595. origin,
  596. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  597. channel_id: T::ChannelId,
  598. num_objects_to_delete: u64,
  599. ) -> DispatchResult {
  600. // check that channel exists
  601. let channel = Self::ensure_channel_exists(&channel_id)?;
  602. // ensure permissions
  603. ensure_actor_authorized_to_update_channel::<T>(
  604. origin,
  605. &actor,
  606. &channel.owner,
  607. )?;
  608. // check that channel videos are 0
  609. ensure!(channel.num_videos == 0, Error::<T>::ChannelContainsVideos);
  610. // get bag id for the channel
  611. let dyn_bag = DynamicBagIdType::<T::MemberId, T::ChannelId>::Channel(channel_id);
  612. let bag_id = storage::BagIdType::from(dyn_bag.clone());
  613. // channel has a dynamic bag associated to it -> remove assets from storage
  614. if let Ok(bag) = T::DataObjectStorage::ensure_bag_exists(&bag_id) {
  615. // ensure that bag size provided is valid
  616. ensure!(
  617. bag.objects_number == num_objects_to_delete,
  618. Error::<T>::InvalidBagSizeSpecified
  619. );
  620. // construct collection of assets to be removed
  621. let assets_to_remove = T::DataObjectStorage::get_data_objects_id(&bag_id);
  622. // remove specified assets from storage
  623. Self::remove_assets_from_storage(
  624. &assets_to_remove,
  625. &channel_id,
  626. &channel.deletion_prize_source_account_id
  627. )?;
  628. // delete channel dynamic bag
  629. Storage::<T>::delete_dynamic_bag(
  630. channel.deletion_prize_source_account_id,
  631. dyn_bag
  632. )?;
  633. }
  634. //
  635. // == MUTATION SAFE ==
  636. //
  637. // remove channel from on chain state
  638. ChannelById::<T>::remove(channel_id);
  639. // deposit event
  640. Self::deposit_event(RawEvent::ChannelDeleted(actor, channel_id));
  641. Ok(())
  642. }
  643. #[weight = 10_000_000] // TODO: adjust weight
  644. pub fn update_channel_censorship_status(
  645. origin,
  646. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  647. channel_id: T::ChannelId,
  648. is_censored: bool,
  649. rationale: Vec<u8>,
  650. ) {
  651. // check that channel exists
  652. let channel = Self::ensure_channel_exists(&channel_id)?;
  653. if channel.is_censored == is_censored {
  654. return Ok(())
  655. }
  656. ensure_actor_authorized_to_censor::<T>(
  657. origin,
  658. &actor,
  659. &channel.owner,
  660. )?;
  661. //
  662. // == MUTATION SAFE ==
  663. //
  664. ChannelById::<T>::mutate(channel_id, |channel| {
  665. channel.is_censored = is_censored
  666. });
  667. // TODO: unset the reward account ? so no revenue can be earned for censored channels?
  668. Self::deposit_event(RawEvent::ChannelCensorshipStatusUpdated(actor, channel_id, is_censored, rationale));
  669. }
  670. #[weight = 10_000_000] // TODO: adjust weight
  671. pub fn create_channel_category(
  672. origin,
  673. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  674. params: ChannelCategoryCreationParameters,
  675. ) {
  676. ensure_actor_authorized_to_manage_categories::<T>(
  677. origin,
  678. &actor
  679. )?;
  680. //
  681. // == MUTATION SAFE ==
  682. //
  683. let category_id = Self::next_channel_category_id();
  684. NextChannelCategoryId::<T>::mutate(|id| *id += T::ChannelCategoryId::one());
  685. let category = ChannelCategory {};
  686. ChannelCategoryById::<T>::insert(category_id, category.clone());
  687. Self::deposit_event(RawEvent::ChannelCategoryCreated(category_id, category, params));
  688. }
  689. #[weight = 10_000_000] // TODO: adjust weight
  690. pub fn update_channel_category(
  691. origin,
  692. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  693. category_id: T::ChannelCategoryId,
  694. params: ChannelCategoryUpdateParameters,
  695. ) {
  696. ensure_actor_authorized_to_manage_categories::<T>(
  697. origin,
  698. &actor
  699. )?;
  700. Self::ensure_channel_category_exists(&category_id)?;
  701. Self::deposit_event(RawEvent::ChannelCategoryUpdated(actor, category_id, params));
  702. }
  703. #[weight = 10_000_000] // TODO: adjust weight
  704. pub fn delete_channel_category(
  705. origin,
  706. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  707. category_id: T::ChannelCategoryId,
  708. ) {
  709. ensure_actor_authorized_to_manage_categories::<T>(
  710. origin,
  711. &actor
  712. )?;
  713. Self::ensure_channel_category_exists(&category_id)?;
  714. ChannelCategoryById::<T>::remove(&category_id);
  715. Self::deposit_event(RawEvent::ChannelCategoryDeleted(actor, category_id));
  716. }
  717. #[weight = 10_000_000] // TODO: adjust weight
  718. pub fn request_channel_transfer(
  719. _origin,
  720. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  721. _request: ChannelOwnershipTransferRequest<T>,
  722. ) {
  723. // requester must be new_owner
  724. Self::not_implemented()?;
  725. }
  726. #[weight = 10_000_000] // TODO: adjust weight
  727. pub fn cancel_channel_transfer_request(
  728. _origin,
  729. _request_id: T::ChannelOwnershipTransferRequestId,
  730. ) {
  731. // origin must be original requester (ie. proposed new channel owner)
  732. Self::not_implemented()?;
  733. }
  734. #[weight = 10_000_000] // TODO: adjust weight
  735. pub fn accept_channel_transfer(
  736. _origin,
  737. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  738. _request_id: T::ChannelOwnershipTransferRequestId,
  739. ) {
  740. // only current owner of channel can approve
  741. Self::not_implemented()?;
  742. }
  743. #[weight = 10_000_000] // TODO: adjust weight
  744. pub fn create_video(
  745. origin,
  746. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  747. channel_id: T::ChannelId,
  748. params: VideoCreationParameters<T>,
  749. ) {
  750. // check that channel exists
  751. let channel = Self::ensure_channel_exists(&channel_id)?;
  752. ensure_actor_authorized_to_update_channel::<T>(
  753. origin,
  754. &actor,
  755. &channel.owner,
  756. )?;
  757. // next video id
  758. let video_id = NextVideoId::<T>::get();
  759. // atomically upload to storage and return the # of uploaded assets
  760. if let Some(upload_assets) = params.assets.as_ref() {
  761. Self::upload_assets_to_storage(
  762. upload_assets,
  763. &channel_id,
  764. &channel.deletion_prize_source_account_id
  765. )?;
  766. }
  767. //
  768. // == MUTATION SAFE ==
  769. //
  770. // create the video struct
  771. let video: Video<T> = VideoRecord {
  772. in_channel: channel_id,
  773. // keep track of which season the video is in if it is an 'episode'
  774. // - prevent removing a video if it is in a season (because order is important)
  775. in_series: None,
  776. /// Whether the curators have censored the video or not.
  777. is_censored: false,
  778. };
  779. // add it to the onchain state
  780. VideoById::<T>::insert(video_id, video);
  781. // Only increment next video id if adding content was successful
  782. NextVideoId::<T>::mutate(|id| *id += T::VideoId::one());
  783. // Add recently added video id to the channel
  784. ChannelById::<T>::mutate(channel_id, |channel| {
  785. channel.num_videos = channel.num_videos.saturating_add(1);
  786. });
  787. Self::deposit_event(RawEvent::VideoCreated(actor, channel_id, video_id, params));
  788. }
  789. #[weight = 10_000_000] // TODO: adjust weight
  790. pub fn update_video(
  791. origin,
  792. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  793. video_id: T::VideoId,
  794. params: VideoUpdateParameters<T>,
  795. ) {
  796. // check that video exists, retrieve corresponding channel id.
  797. let video = Self::ensure_video_exists(&video_id)?;
  798. let channel_id = video.in_channel;
  799. let channel = ChannelById::<T>::get(&channel_id);
  800. ensure_actor_authorized_to_update_channel::<T>(
  801. origin,
  802. &actor,
  803. &channel.owner,
  804. )?;
  805. // remove specified assets from channel bag in storage
  806. Self::remove_assets_from_storage(&params.assets_to_remove, &channel_id, &channel.deletion_prize_source_account_id)?;
  807. // atomically upload to storage and return the # of uploaded assets
  808. if let Some(upload_assets) = params.assets_to_upload.as_ref() {
  809. Self::upload_assets_to_storage(
  810. upload_assets,
  811. &channel_id,
  812. &channel.deletion_prize_source_account_id
  813. )?;
  814. }
  815. //
  816. // == MUTATION SAFE ==
  817. //
  818. Self::deposit_event(RawEvent::VideoUpdated(actor, video_id, params));
  819. }
  820. #[weight = 10_000_000] // TODO: adjust weight
  821. pub fn delete_video(
  822. origin,
  823. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  824. video_id: T::VideoId,
  825. assets_to_remove: BTreeSet<DataObjectId<T>>,
  826. ) {
  827. // check that video exists
  828. let video = Self::ensure_video_exists(&video_id)?;
  829. // get information regarding channel
  830. let channel_id = video.in_channel;
  831. let channel = ChannelById::<T>::get(channel_id);
  832. ensure_actor_authorized_to_update_channel::<T>(
  833. origin,
  834. &actor,
  835. // The channel owner will be..
  836. &channel.owner,
  837. )?;
  838. // ensure video can be removed
  839. Self::ensure_video_can_be_removed(&video)?;
  840. // remove specified assets from channel bag in storage
  841. Self::remove_assets_from_storage(&assets_to_remove, &channel_id, &channel.deletion_prize_source_account_id)?;
  842. //
  843. // == MUTATION SAFE ==
  844. //
  845. // Remove video
  846. VideoById::<T>::remove(video_id);
  847. // Decrease video count for the channel
  848. ChannelById::<T>::mutate(channel_id, |channel| {
  849. channel.num_videos = channel.num_videos.saturating_sub(1)
  850. });
  851. Self::deposit_event(RawEvent::VideoDeleted(actor, video_id));
  852. }
  853. #[weight = 10_000_000] // TODO: adjust weight
  854. pub fn create_playlist(
  855. _origin,
  856. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  857. _channel_id: T::ChannelId,
  858. _params: PlaylistCreationParameters,
  859. ) {
  860. Self::not_implemented()?;
  861. }
  862. #[weight = 10_000_000] // TODO: adjust weight
  863. pub fn update_playlist(
  864. _origin,
  865. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  866. _playlist: T::PlaylistId,
  867. _params: PlaylistUpdateParameters,
  868. ) {
  869. Self::not_implemented()?;
  870. }
  871. #[weight = 10_000_000] // TODO: adjust weight
  872. pub fn delete_playlist(
  873. _origin,
  874. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  875. _channel_id: T::ChannelId,
  876. _playlist: T::PlaylistId,
  877. ) {
  878. Self::not_implemented()?;
  879. }
  880. #[weight = 10_000_000] // TODO: adjust weight
  881. pub fn set_featured_videos(
  882. origin,
  883. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  884. list: Vec<T::VideoId>
  885. ) {
  886. // can only be set by lead
  887. ensure_actor_authorized_to_set_featured_videos::<T>(
  888. origin,
  889. &actor,
  890. )?;
  891. //
  892. // == MUTATION SAFE ==
  893. //
  894. Self::deposit_event(RawEvent::FeaturedVideosSet(actor, list));
  895. }
  896. #[weight = 10_000_000] // TODO: adjust weight
  897. pub fn create_video_category(
  898. origin,
  899. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  900. params: VideoCategoryCreationParameters,
  901. ) {
  902. ensure_actor_authorized_to_manage_categories::<T>(
  903. origin,
  904. &actor
  905. )?;
  906. //
  907. // == MUTATION SAFE ==
  908. //
  909. let category_id = Self::next_video_category_id();
  910. NextVideoCategoryId::<T>::mutate(|id| *id += T::VideoCategoryId::one());
  911. let category = VideoCategory {};
  912. VideoCategoryById::<T>::insert(category_id, category);
  913. Self::deposit_event(RawEvent::VideoCategoryCreated(actor, category_id, params));
  914. }
  915. #[weight = 10_000_000] // TODO: adjust weight
  916. pub fn update_video_category(
  917. origin,
  918. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  919. category_id: T::VideoCategoryId,
  920. params: VideoCategoryUpdateParameters,
  921. ) {
  922. ensure_actor_authorized_to_manage_categories::<T>(
  923. origin,
  924. &actor
  925. )?;
  926. Self::ensure_video_category_exists(&category_id)?;
  927. Self::deposit_event(RawEvent::VideoCategoryUpdated(actor, category_id, params));
  928. }
  929. #[weight = 10_000_000] // TODO: adjust weight
  930. pub fn delete_video_category(
  931. origin,
  932. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  933. category_id: T::VideoCategoryId,
  934. ) {
  935. ensure_actor_authorized_to_manage_categories::<T>(
  936. origin,
  937. &actor
  938. )?;
  939. Self::ensure_video_category_exists(&category_id)?;
  940. VideoCategoryById::<T>::remove(&category_id);
  941. Self::deposit_event(RawEvent::VideoCategoryDeleted(actor, category_id));
  942. }
  943. #[weight = 10_000_000] // TODO: adjust weight
  944. pub fn create_person(
  945. _origin,
  946. _actor: PersonActor<T::MemberId, T::CuratorId>,
  947. _params: PersonCreationParameters<StorageAssets<T>>,
  948. ) {
  949. Self::not_implemented()?;
  950. }
  951. #[weight = 10_000_000] // TODO: adjust weight
  952. pub fn update_person(
  953. _origin,
  954. _actor: PersonActor<T::MemberId, T::CuratorId>,
  955. _person: T::PersonId,
  956. _params: PersonUpdateParameters<StorageAssets<T>>,
  957. ) {
  958. Self::not_implemented()?;
  959. }
  960. #[weight = 10_000_000] // TODO: adjust weight
  961. pub fn delete_person(
  962. _origin,
  963. _actor: PersonActor<T::MemberId, T::CuratorId>,
  964. _person: T::PersonId,
  965. ) {
  966. Self::not_implemented()?;
  967. }
  968. #[weight = 10_000_000] // TODO: adjust weight
  969. pub fn add_person_to_video(
  970. _origin,
  971. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  972. _video_id: T::VideoId,
  973. _person: T::PersonId
  974. ) {
  975. Self::not_implemented()?;
  976. }
  977. #[weight = 10_000_000] // TODO: adjust weight
  978. pub fn remove_person_from_video(
  979. _origin,
  980. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  981. _video_id: T::VideoId
  982. ) {
  983. Self::not_implemented()?;
  984. }
  985. #[weight = 10_000_000] // TODO: adjust weight
  986. pub fn update_video_censorship_status(
  987. origin,
  988. actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  989. video_id: T::VideoId,
  990. is_censored: bool,
  991. rationale: Vec<u8>,
  992. ) {
  993. // check that video exists
  994. let video = Self::ensure_video_exists(&video_id)?;
  995. if video.is_censored == is_censored {
  996. return Ok(())
  997. }
  998. ensure_actor_authorized_to_censor::<T>(
  999. origin,
  1000. &actor,
  1001. // The channel owner will be..
  1002. &Self::channel_by_id(video.in_channel).owner,
  1003. )?;
  1004. //
  1005. // == MUTATION SAFE ==
  1006. //
  1007. // update
  1008. VideoById::<T>::mutate(video_id, |video| {
  1009. video.is_censored = is_censored;
  1010. });
  1011. Self::deposit_event(RawEvent::VideoCensorshipStatusUpdated(actor, video_id, is_censored, rationale));
  1012. }
  1013. #[weight = 10_000_000] // TODO: adjust weight
  1014. pub fn create_series(
  1015. _origin,
  1016. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  1017. _channel_id: T::ChannelId,
  1018. _params: SeriesParameters<T::VideoId, StorageAssets<T>>
  1019. ) {
  1020. Self::not_implemented()?;
  1021. }
  1022. #[weight = 10_000_000] // TODO: adjust weight
  1023. pub fn update_series(
  1024. _origin,
  1025. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  1026. _channel_id: T::ChannelId,
  1027. _params: SeriesParameters<T::VideoId, StorageAssets<T>>
  1028. ) {
  1029. Self::not_implemented()?;
  1030. }
  1031. #[weight = 10_000_000] // TODO: adjust weight
  1032. pub fn delete_series(
  1033. _origin,
  1034. _actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  1035. _series: T::SeriesId,
  1036. ) {
  1037. Self::not_implemented()?;
  1038. }
  1039. }
  1040. }
  1041. impl<T: Trait> Module<T> {
  1042. /// Ensure `CuratorGroup` under given id exists
  1043. fn ensure_curator_group_under_given_id_exists(
  1044. curator_group_id: &T::CuratorGroupId,
  1045. ) -> Result<(), Error<T>> {
  1046. ensure!(
  1047. <CuratorGroupById<T>>::contains_key(curator_group_id),
  1048. Error::<T>::CuratorGroupDoesNotExist
  1049. );
  1050. Ok(())
  1051. }
  1052. /// Ensure `CuratorGroup` under given id exists, return corresponding one
  1053. fn ensure_curator_group_exists(
  1054. curator_group_id: &T::CuratorGroupId,
  1055. ) -> Result<CuratorGroup<T>, Error<T>> {
  1056. Self::ensure_curator_group_under_given_id_exists(curator_group_id)?;
  1057. Ok(Self::curator_group_by_id(curator_group_id))
  1058. }
  1059. fn ensure_channel_exists(channel_id: &T::ChannelId) -> Result<Channel<T>, Error<T>> {
  1060. ensure!(
  1061. ChannelById::<T>::contains_key(channel_id),
  1062. Error::<T>::ChannelDoesNotExist
  1063. );
  1064. Ok(ChannelById::<T>::get(channel_id))
  1065. }
  1066. fn ensure_video_exists(video_id: &T::VideoId) -> Result<Video<T>, Error<T>> {
  1067. ensure!(
  1068. VideoById::<T>::contains_key(video_id),
  1069. Error::<T>::VideoDoesNotExist
  1070. );
  1071. Ok(VideoById::<T>::get(video_id))
  1072. }
  1073. // Ensure given video is not in season
  1074. fn ensure_video_can_be_removed(video: &Video<T>) -> DispatchResult {
  1075. ensure!(video.in_series.is_none(), Error::<T>::VideoInSeason);
  1076. Ok(())
  1077. }
  1078. fn ensure_channel_category_exists(
  1079. channel_category_id: &T::ChannelCategoryId,
  1080. ) -> Result<ChannelCategory, Error<T>> {
  1081. ensure!(
  1082. ChannelCategoryById::<T>::contains_key(channel_category_id),
  1083. Error::<T>::CategoryDoesNotExist
  1084. );
  1085. Ok(ChannelCategoryById::<T>::get(channel_category_id))
  1086. }
  1087. fn ensure_video_category_exists(
  1088. video_category_id: &T::VideoCategoryId,
  1089. ) -> Result<VideoCategory, Error<T>> {
  1090. ensure!(
  1091. VideoCategoryById::<T>::contains_key(video_category_id),
  1092. Error::<T>::CategoryDoesNotExist
  1093. );
  1094. Ok(VideoCategoryById::<T>::get(video_category_id))
  1095. }
  1096. fn pick_upload_parameters_from_assets(
  1097. assets: &StorageAssets<T>,
  1098. channel_id: &T::ChannelId,
  1099. prize_source_account: &T::AccountId,
  1100. ) -> UploadParameters<T> {
  1101. // dynamic bag for a media object
  1102. let dyn_bag = DynamicBagIdType::<T::MemberId, T::ChannelId>::Channel(*channel_id);
  1103. let bag_id = BagIdType::from(dyn_bag.clone());
  1104. if T::DataObjectStorage::ensure_bag_exists(&bag_id).is_err() {
  1105. // create_dynamic_bag checks automatically satifsfied with None as second parameter
  1106. Storage::<T>::create_dynamic_bag(dyn_bag, None).unwrap();
  1107. }
  1108. UploadParametersRecord {
  1109. bag_id,
  1110. object_creation_list: assets.object_creation_list.clone(),
  1111. deletion_prize_source_account_id: prize_source_account.clone(),
  1112. expected_data_size_fee: assets.expected_data_size_fee,
  1113. }
  1114. }
  1115. fn actor_to_channel_owner(
  1116. actor: &ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
  1117. ) -> ActorToChannelOwnerResult<T> {
  1118. match actor {
  1119. // Lead should use their member or curator role to create channels
  1120. ContentActor::Lead => Err(Error::<T>::ActorCannotOwnChannel),
  1121. ContentActor::Curator(curator_group_id, _curator_id) => {
  1122. Ok(ChannelOwner::CuratorGroup(*curator_group_id))
  1123. }
  1124. ContentActor::Member(member_id) => Ok(ChannelOwner::Member(*member_id)),
  1125. }
  1126. }
  1127. fn bag_id_for_channel(channel_id: &T::ChannelId) -> storage::BagId<T> {
  1128. // retrieve bag id from channel id
  1129. let dyn_bag = DynamicBagIdType::<T::MemberId, T::ChannelId>::Channel(*channel_id);
  1130. BagIdType::from(dyn_bag)
  1131. }
  1132. fn not_implemented() -> DispatchResult {
  1133. Err(Error::<T>::FeatureNotImplemented.into())
  1134. }
  1135. fn upload_assets_to_storage(
  1136. assets: &StorageAssets<T>,
  1137. channel_id: &T::ChannelId,
  1138. prize_source_account: &T::AccountId,
  1139. ) -> DispatchResult {
  1140. // construct upload params
  1141. let upload_params =
  1142. Self::pick_upload_parameters_from_assets(assets, channel_id, prize_source_account);
  1143. // attempt to upload objects att
  1144. Storage::<T>::upload_data_objects(upload_params.clone())?;
  1145. Ok(())
  1146. }
  1147. fn remove_assets_from_storage(
  1148. assets: &BTreeSet<DataObjectId<T>>,
  1149. channel_id: &T::ChannelId,
  1150. prize_source_account: &T::AccountId,
  1151. ) -> DispatchResult {
  1152. // remove assets if any
  1153. if !assets.is_empty() {
  1154. Storage::<T>::delete_data_objects(
  1155. prize_source_account.clone(),
  1156. Self::bag_id_for_channel(&channel_id),
  1157. assets.clone(),
  1158. )?;
  1159. }
  1160. Ok(())
  1161. }
  1162. }
  1163. decl_event!(
  1164. pub enum Event<T>
  1165. where
  1166. ContentActor = ContentActor<
  1167. <T as ContentActorAuthenticator>::CuratorGroupId,
  1168. <T as ContentActorAuthenticator>::CuratorId,
  1169. <T as common::MembershipTypes>::MemberId,
  1170. >,
  1171. CuratorGroupId = <T as ContentActorAuthenticator>::CuratorGroupId,
  1172. CuratorId = <T as ContentActorAuthenticator>::CuratorId,
  1173. VideoId = <T as Trait>::VideoId,
  1174. VideoCategoryId = <T as Trait>::VideoCategoryId,
  1175. ChannelId = <T as storage::Trait>::ChannelId,
  1176. ChannelCategoryId = <T as Trait>::ChannelCategoryId,
  1177. ChannelOwnershipTransferRequestId = <T as Trait>::ChannelOwnershipTransferRequestId,
  1178. PlaylistId = <T as Trait>::PlaylistId,
  1179. SeriesId = <T as Trait>::SeriesId,
  1180. PersonId = <T as Trait>::PersonId,
  1181. ChannelOwnershipTransferRequest = ChannelOwnershipTransferRequest<T>,
  1182. Series = Series<<T as storage::Trait>::ChannelId, <T as Trait>::VideoId>,
  1183. Channel = Channel<T>,
  1184. DataObjectId = DataObjectId<T>,
  1185. IsCensored = bool,
  1186. ChannelCreationParameters = ChannelCreationParameters<T>,
  1187. ChannelUpdateParameters = ChannelUpdateParameters<T>,
  1188. VideoCreationParameters = VideoCreationParameters<T>,
  1189. VideoUpdateParameters = VideoUpdateParameters<T>,
  1190. StorageAssets = StorageAssets<T>,
  1191. {
  1192. // Curators
  1193. CuratorGroupCreated(CuratorGroupId),
  1194. CuratorGroupStatusSet(CuratorGroupId, bool /* active status */),
  1195. CuratorAdded(CuratorGroupId, CuratorId),
  1196. CuratorRemoved(CuratorGroupId, CuratorId),
  1197. // Channels
  1198. ChannelCreated(ContentActor, ChannelId, Channel, ChannelCreationParameters),
  1199. ChannelUpdated(ContentActor, ChannelId, Channel, ChannelUpdateParameters),
  1200. ChannelAssetsRemoved(ContentActor, ChannelId, BTreeSet<DataObjectId>, Channel),
  1201. ChannelCensorshipStatusUpdated(
  1202. ContentActor,
  1203. ChannelId,
  1204. IsCensored,
  1205. Vec<u8>, /* rationale */
  1206. ),
  1207. // Channel Ownership Transfers
  1208. ChannelOwnershipTransferRequested(
  1209. ContentActor,
  1210. ChannelOwnershipTransferRequestId,
  1211. ChannelOwnershipTransferRequest,
  1212. ),
  1213. ChannelOwnershipTransferRequestWithdrawn(ContentActor, ChannelOwnershipTransferRequestId),
  1214. ChannelOwnershipTransferred(ContentActor, ChannelOwnershipTransferRequestId),
  1215. // Channel Categories
  1216. ChannelCategoryCreated(
  1217. ChannelCategoryId,
  1218. ChannelCategory,
  1219. ChannelCategoryCreationParameters,
  1220. ),
  1221. ChannelCategoryUpdated(
  1222. ContentActor,
  1223. ChannelCategoryId,
  1224. ChannelCategoryUpdateParameters,
  1225. ),
  1226. ChannelCategoryDeleted(ContentActor, ChannelCategoryId),
  1227. // Videos
  1228. VideoCategoryCreated(
  1229. ContentActor,
  1230. VideoCategoryId,
  1231. VideoCategoryCreationParameters,
  1232. ),
  1233. VideoCategoryUpdated(ContentActor, VideoCategoryId, VideoCategoryUpdateParameters),
  1234. VideoCategoryDeleted(ContentActor, VideoCategoryId),
  1235. VideoCreated(ContentActor, ChannelId, VideoId, VideoCreationParameters),
  1236. VideoUpdated(ContentActor, VideoId, VideoUpdateParameters),
  1237. VideoDeleted(ContentActor, VideoId),
  1238. VideoCensorshipStatusUpdated(
  1239. ContentActor,
  1240. VideoId,
  1241. IsCensored,
  1242. Vec<u8>, /* rationale */
  1243. ),
  1244. // Featured Videos
  1245. FeaturedVideosSet(ContentActor, Vec<VideoId>),
  1246. // Video Playlists
  1247. PlaylistCreated(ContentActor, PlaylistId, PlaylistCreationParameters),
  1248. PlaylistUpdated(ContentActor, PlaylistId, PlaylistUpdateParameters),
  1249. PlaylistDeleted(ContentActor, PlaylistId),
  1250. // Series
  1251. SeriesCreated(
  1252. ContentActor,
  1253. SeriesId,
  1254. StorageAssets,
  1255. SeriesParameters<VideoId, StorageAssets>,
  1256. Series,
  1257. ),
  1258. SeriesUpdated(
  1259. ContentActor,
  1260. SeriesId,
  1261. StorageAssets,
  1262. SeriesParameters<VideoId, StorageAssets>,
  1263. Series,
  1264. ),
  1265. SeriesDeleted(ContentActor, SeriesId),
  1266. // Persons
  1267. PersonCreated(
  1268. ContentActor,
  1269. PersonId,
  1270. StorageAssets,
  1271. PersonCreationParameters<StorageAssets>,
  1272. ),
  1273. PersonUpdated(
  1274. ContentActor,
  1275. PersonId,
  1276. StorageAssets,
  1277. PersonUpdateParameters<StorageAssets>,
  1278. ),
  1279. PersonDeleted(ContentActor, PersonId),
  1280. ChannelDeleted(ContentActor, ChannelId),
  1281. }
  1282. );