DUMMY.pm 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package Trog::Data::DUMMY;
  2. use strict;
  3. use warnings;
  4. no warnings 'experimental';
  5. use feature qw{signatures};
  6. =head1 QUERY FORMAT
  7. The $query_language and $query_help variables are presented to the user as to how to use the search box in the tCMS header.
  8. =cut
  9. our $query_language = 'Perl Regex in Quotemeta';
  10. our $query_help = 'https://perldoc.perl.org/functions/quotemeta.html';
  11. =head1 POST STRUCTURE
  12. Posts generally need to have the following:
  13. data: Brief description of content, or the content itself.
  14. content_type: What this content actually is. Used to filter into the appropriate pages.
  15. href: Primary link. This is the subject of a news post, or a link to the item itself. Can be local or remote.
  16. local_href: Backup link. Automatically created link to a static cache of the content.
  17. title: Title of the content. Used as link name for the 'href' attribute.
  18. user: User was banned for this post
  19. id: Internal identifier in datastore for the post.
  20. tags: array ref of appropriate tags.
  21. created: timestamp of creation of this version of the post
  22. version: revision # of this post.
  23. =cut
  24. my $example_posts = [
  25. {
  26. content_type => "text/html",
  27. data => "Here, caveman",
  28. href => '/',
  29. local_href => "/assets/today.html",
  30. title => 'Example Post',
  31. user => 'Nobody',
  32. id => 665,
  33. tags => ['news', 'public'],
  34. created => time(),
  35. version => 0,
  36. },
  37. {
  38. content_type => "text/html",
  39. data => "Is amazing",
  40. href => '/',
  41. local_href => "/assets/blog/Muh Blog.html",
  42. title => 'Muh Blog',
  43. user => 'Nobody',
  44. id => 666,
  45. tags => ['blog', 'public'],
  46. created => time(),
  47. version => 0,
  48. },
  49. {
  50. content_type => "text/html",
  51. data => "Vote for Nobody, nobody really cares!",
  52. href => '/',
  53. local_href => "/assets/about/Nobody.html",
  54. title => 'Nobody',
  55. user => 'Nobody',
  56. id => 669,
  57. tags => ['about', 'profile', 'public'],
  58. created => time(),
  59. version => 0,
  60. background_image => '/img/sys/testpattern.jpg',
  61. preview => '/img/avatar/humm.gif',
  62. },
  63. {
  64. content_type => "image/gif",
  65. data => "Default avatar for new users",
  66. href => "/img/avatar/humm.gif",
  67. local_href => "/img/avatar/humm.gif",
  68. title => "humm.gif",
  69. user => 'Nobody',
  70. id => 420,
  71. tags => ['image', 'files', 'profile-image', 'public'],
  72. created => time(),
  73. version => 0,
  74. preview => '/img/avatar/humm.gif',
  75. },
  76. {
  77. content_type => "image/jpeg",
  78. data => "Test Pattern",
  79. href => "/img/sys/testpattern.jpg",
  80. local_href => "/img/sys/testpattern.jpg",
  81. title => "testpattern.jpg",
  82. user => 'Nobody',
  83. id => 90210,
  84. tags => ['image', 'files', 'public'],
  85. created => time(),
  86. version => 0,
  87. preview => '/img/sys/testpattern.jpg',
  88. },
  89. {
  90. content_type => "audio/mpeg",
  91. data => "Test recording for tCMS",
  92. href => "/assets/audio/test.mp3",
  93. local_href => "/assets/audio/test.mp3",
  94. title => "test.mp3",
  95. user => "Nobody",
  96. id => 111,
  97. tags => ["audio", "files", 'public'],
  98. created => time(),
  99. version => 0,
  100. preview => '/img/sys/testpattern.jpg',
  101. },
  102. {
  103. content_type => "video/ogg",
  104. data => "Test video for tCMS",
  105. href => "/assets/video/test.ogv",
  106. local_href => "/assets/video/test.ogv",
  107. title => "test.ogv",
  108. user => "Nobody",
  109. id => "222",
  110. tags => ["video", "files", 'public'],
  111. created => time(),
  112. version => 0,
  113. preview => '/img/sys/testpattern.jpg',
  114. },
  115. {
  116. content_type => 'text/plain',
  117. data => "Admin ACL",
  118. href => "/config",
  119. local_href => '/config',
  120. title => 'Administrative Posts',
  121. user => 'Nobody',
  122. id => "900",
  123. aclname => 'admin',
  124. tags => ['series', 'private', 'admin'],
  125. created => time(),
  126. version => 0,
  127. preview => '/img/sys/testpattern.jpg',
  128. },
  129. {
  130. content_type => "image/svg",
  131. data => "tCMS Logo",
  132. href => "/img/icon/tCMS.svg",
  133. local_href => "/img/icon/tCMS.svg",
  134. title => "tCMS.svg",
  135. user => 'Nobody',
  136. id => 90211,
  137. tags => ['image', 'files', 'admin'],
  138. created => time(),
  139. version => 0,
  140. preview => '/img/icon/tCMS.svg',
  141. },
  142. ];
  143. =head1 CONSTRUCTOR
  144. =head2 new(Config::Simple $config)
  145. Try not to do expensive things here.
  146. =cut
  147. sub new ($class, $config) {
  148. $config = $config->vars();
  149. $config->{lang} = $query_language;
  150. $config->{help} = $query_help;
  151. return bless($config,__PACKAGE__);
  152. }
  153. =head1 METHODS
  154. =head2 get(%request)
  155. Queries the data model in the way a "real" data model module ought to.
  156. id => Filter down to just the post by ID. May be subsequently filtered by ACL, resulting in a 404 (which is good, as it does not disclose info).
  157. tags => ARRAYREF of tags, any one of which is required to give a result. If none are passed, no filtering is performed.
  158. acls => ARRAYREF of acl tags, any one of which is required to give result. Filter applies after tags. 'admin' ACL being present skips this filter.
  159. page => Offset multiplier for pagination.
  160. limit => Offset for pagination.
  161. like => Search query, as might be passed in the search bar.
  162. =cut
  163. # These have to be sorted as requested by the client
  164. sub get ($self, %request) {
  165. my @filtered = @$example_posts;
  166. # If an ID is passed, just get that
  167. @filtered = grep { $_->{id} eq $request{id} } @filtered if $request{id};
  168. # Next, handle the query, tags and ACLs
  169. @filtered = grep { my $tags = $_->{tags}; grep { my $t = $_; grep {$t eq $_ } @{$request{tags}} } @$tags } @filtered if @{$request{tags}};
  170. @filtered = grep { my $tags = $_->{tags}; grep { my $t = $_; grep {$t eq $_ } @{$request{acls}} } @$tags } @filtered unless grep { $_ eq 'admin' } @{$request{acls}};
  171. @filtered = grep { $_->{data} =~ m/\Q$request{like}\E/i } @filtered if $request{like};
  172. # Finally, paginate
  173. my $offset = int($request{limit});
  174. $offset = @filtered < $offset ? @filtered : $offset;
  175. my $pages = int(scalar(@filtered) / ($offset || 1) );
  176. @filtered = splice(@filtered, ( int($request{page}) -1) * $offset, $offset) if $request{page} && $request{limit};
  177. # Next, go ahead and build the "post type"
  178. @filtered = _add_post_type(@filtered);
  179. # Next, add the type of post this is
  180. @filtered = _add_media_type(@filtered);
  181. # Finally, add visibility
  182. @filtered = _add_visibility(@filtered);
  183. return ($pages,\@filtered);
  184. }
  185. sub total_posts {
  186. return scalar(@$example_posts);
  187. }
  188. sub _add_post_type (@posts) {
  189. return map {
  190. my $post = $_;
  191. my $type = 'file';
  192. $type = 'blog' if grep { $_ eq 'blog' } @{$post->{tags}};
  193. $type = 'microblog' if grep { $_ eq 'news' } @{$post->{tags}};
  194. $type = 'profile' if grep { $_ eq 'profile' } @{$post->{tags}};
  195. $type = 'series' if grep { $_ eq 'series' } @{$post->{tags}};
  196. $post->{type} = $type;
  197. $post
  198. } @posts;
  199. }
  200. sub _add_media_type (@posts) {
  201. return map {
  202. my $post = $_;
  203. $post->{is_video} = 1 if $post->{content_type} =~ m/^video\//;
  204. $post->{is_audio} = 1 if $post->{content_type} =~ m/^audio\//;
  205. $post->{is_image} = 1 if $post->{content_type} =~ m/^image\//;
  206. $post
  207. } @posts;
  208. }
  209. sub _add_visibility (@posts) {
  210. return map {
  211. my $post = $_;
  212. my @visibilities = grep { my $tag = $_; grep { $_ eq $tag } qw{private unlisted public} } @{$post->{tags}};
  213. $post->{visibility} = $visibilities[0];
  214. $post
  215. } @posts;
  216. }
  217. sub add ($self, @posts) {
  218. require UUID::Tiny;
  219. foreach my $post (@posts) {
  220. my $uuid = UUID::Tiny::create_uuid_as_string(UUID::Tiny::UUID_V1, UUID::Tiny::UUID_NS_DNS);
  221. $post->{id} = $uuid;
  222. push @$example_posts, $post;
  223. }
  224. return 1;
  225. }
  226. sub update($self, @posts) {
  227. foreach my $update (@posts) {
  228. foreach my $post (0..scalar(@$example_posts)) {
  229. next unless $example_posts->[$post]->{id} eq $update->{id};
  230. $example_posts->[$post] = $update;
  231. }
  232. }
  233. return 1;
  234. }
  235. sub delete($self, @posts) {
  236. foreach my $update (@posts) {
  237. @$example_posts = grep { $_->{id} ne $update->{id} } @$example_posts;
  238. }
  239. return 1;
  240. }
  241. 1;