casbin-test.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. <?php
  2. require_once '../../vendor/autoload.php';
  3. require_once '../config.php';
  4. require_once '../config.php';
  5. use Casbin\Enforcer;
  6. use CasbinAdapter\Medoo\Adapter as DatabaseAdapter;
  7. define("IDPrefixTranslation" , "it_");
  8. define("IDPrefixChannel", "ic_");
  9. define("IDPrefixArticle", "ia_");
  10. define("IDPrefixUser" , "iu_");
  11. define("IDPrefixOrg" , "io_");
  12. define("IDPrefixOrgGroup", "iog_");
  13. // 资源分组:版本风格
  14. define("ResStudio" , "s_studio");
  15. // 资源分组:版本风格
  16. define("ResChannel" , "s_channel");
  17. // 资源分组:文章
  18. define("ResArticle" , "s_article");
  19. // 资源分组:文集
  20. define("ResArticle" , "s_collection");
  21. // 资源分组:译文(版本风格 + 文章)句子库
  22. define("ResTranslation" , "s_translation");
  23. // 资源分组:逐词译段落
  24. define("ResArticle" , "s_wbw");
  25. // 资源分组:术语
  26. define("ResArticle" , "s_term");
  27. // 资源分组:用户字典
  28. define("ResArticle" , "s_userdict");
  29. // 组织拥有者 转让
  30. define("RoleOrgOwner" , "r_owner");
  31. // 组织管理员 创建 删除 修改 文章/文集模版 channel group
  32. define("RoleOrgAdmin" , "r_admin");
  33. // 组织编辑 修改译文
  34. define("RoleOrgEditor" , "r_editor");
  35. // 组织成员 读取任意资源
  36. define("RoleOrgMember" , "r_member");
  37. // 组织访客(比如未注册用户) 只读取公开资源
  38. define("RoleOrgVisitor" , "r_visitor");
  39. // 权限:角色分组
  40. define("GroupRole" , "g");
  41. // 权限:资源分组
  42. define("GroupRes" , "g2");
  43. // 权限:阅读权限
  44. define("PermRead" , "p_read");
  45. // 权限:翻译权限
  46. define("PermTrans" , "p_trans");
  47. // 权限:修改权限
  48. define("PermWrite" , "p_write");
  49. // 权限:创建
  50. define("PermCreate" , "p_create");
  51. // 权限:删除
  52. define("PermDelete" , "p_delete");
  53. // 权限:修改
  54. define("PermUpdate" , "p_update");
  55. $config = [
  56. 'database_type' => Database["type"],
  57. 'server' => Database['server'],
  58. 'database_name' => Database['name'],
  59. 'username' => Database['user'],
  60. 'password' => Database['password'],
  61. 'port' => Database['port'],
  62. ];
  63. $adapter = DatabaseAdapter::newAdapter($config);
  64. $e = new Enforcer('rbac.model.conf', $adapter);
  65. {
  66. /*
  67. * 按照业务逻辑测试验证
  68. */
  69. echo("// 创建新用户 zhang3,等于同时创建了 Org: zhang3,只是 OrgID 和 UserID 相同");
  70. CreateOrg("zhang3", "zhang3", $e);
  71. echo("// 用户 zhang3 创建了 版本风格 chinese-01");
  72. CreateChannel("chinese-01", "zhang3", $e);
  73. echo("// 将channel chinese-01 加入到组织 zhang3/group1 里");
  74. AddChannelReader1("chinese-01", "wang5", "group1", $e);
  75. echo("// 用户 zhang3 创建了 版本风格 chinese-02");
  76. CreateChannel("chinese-02", "zhang3", $e);
  77. echo("// 用户 zhang3 在组织 zhang3 下创建了 文章模板 article-01");
  78. CreateArticle("article-01", "zhang3", $e);
  79. echo("// 用户 zhang3 基于 版本风格 chinese-01 和 文章 article-01 创建了 译文 chinses-01+article-01");
  80. CreateTranslation("chinese-01", "article-01", "zhang3", $e);
  81. echo("// 用户 zhang3 基于 版本风格 chinese-02 和 文章 article-01 创建了 译文 chinses-02+article-01");
  82. CreateTranslation("chinese-02", "article-01", "zhang3", $e);
  83. testPermission("zhang3", $e);
  84. echo("// 创建新用户 li4,等于同时创建了 Org: li4,只是 OrgID 和 UserID 相同");
  85. CreateOrg("li4", "li4", $e);
  86. testPermission("li4", $e);
  87. //echo("// 将用户 li4 加入到组织 zhang3 里");
  88. //AddOrgMember("zhang3", "li4", $e);
  89. echo("// 将用户 li4 加入到组织 zhang3/group1 里");
  90. AddOrgGroup("zhang3","group1", "li4", $e );
  91. testPermission("li4", $e);
  92. echo("// 创建新用户 wang5,等于同时创建了 Org: wang5,只是 OrgID 和 UserID 相同");
  93. CreateOrg("wang5", "wang5", $e);
  94. testPermission("wang5", $e);
  95. echo("// 将 chinese-01 分享给 wang5,只读 ");
  96. AddChannelReader("chinese-01", "zhang3", "wang5", $e);
  97. echo("// 将 article-01 分享给 wang5,只读 ");
  98. AddArticleReader("article-01", "zhang3", "wang5", $e);
  99. testPermission("wang5", $e);
  100. echo("// 将 chinese-01 分享给 wang5,可以翻译 ");
  101. AddChannelTranslator("chinese-01", "zhang3", "wang5", $e);
  102. testPermission("wang5", $e);
  103. echo("// 创建新用户 zhao6,等于同时创建了 Org: zhao6,只是 OrgID 和 UserID 相同");
  104. CreateOrg("zhao6", "zhao6", $e);
  105. testPermission("zhao6", $e);
  106. echo("// 将 译文 chinese-01+article-01 分享给 zhao6,翻译权限");
  107. AddTranslationTranslator("chinese-01", "article-01", "zhang3", "zhao6", $e);
  108. echo("// 将 译文 chinese-02+article-01 分享给 zhao6,查看权限");
  109. AddTranslationReader("chinese-02", "article-01", "zhang3", "zhao6", $e);
  110. testPermission("zhao6", $e);
  111. }
  112. function testPermission( $userID , $e ) {
  113. echo("\n");
  114. echo("// ---- $userID 是否有权限 查看 组织 zhang3 的文章模板 article-01?");
  115. $r_a = UserCanReadArticle($userID, "zhang3", "article-01", $e);
  116. echo($r_a."\n");
  117. echo("// ---- $userID 是否有权限 查看 组织 zhang3 的版本风格 chinese-01?");
  118. $r_c= UserCanReadChannel($userID, "zhang3", "chinese-01", $e);
  119. echo($r_c ." \n" );
  120. echo("// ---- $userID 是否有权限 修改 组织 zhang3 的版本风格 chinese-01?");
  121. $r_c_w= UserCanWriteChannel($userID, "zhang3", "chinese-01", $e);
  122. echo($r_c_w." \n");
  123. echo("// ---- $userID 是否有权限 查看 组织 zhang3 的版本风格 chinese-02?");
  124. $r_c02= UserCanReadChannel($userID, "zhang3", "chinese-02", $e);
  125. echo(" $r_c02\n");
  126. echo("// ---- $userID 是否有权限 修改 组织 zhang3 的版本风格 chinese-02?");
  127. $r_c_w02= UserCanWriteChannel($userID, "zhang3", "chinese-02", $e);
  128. echo(" $r_c_w02 \n");
  129. echo("// ---- $userID 是否有权限 基于 组织 zhang3 的版本风格 chinese-01 进行翻译?");
  130. $r_t= UserCanTranslateChannel($userID, "zhang3", "chinese-01", $e);
  131. echo(" $r_t \n");
  132. echo("// ---- $userID 是否能查看 组织 zhang3 的译文 chinese-01+article-01?");
  133. $r_tt= UserCanReadTranslation($userID, "zhang3", "chinese-01", "article-01", $e);
  134. echo(" $r_tt \n");
  135. echo("// ---- $userID 是否能翻译 组织 zhang3 的译文 chinese-01+article-01?");
  136. $r_tt1= UserCanTranslateTranslation($userID, "zhang3", "chinese-01", "article-01", $e);
  137. echo(" $r_tt1 \n" );
  138. echo("// ---- $userID 是否能查看 组织 zhang3 的译文 chinese-02+article-01?");
  139. $r1_tt= UserCanReadTranslation($userID, "zhang3", "chinese-02", "article-01", $e);
  140. echo(" $r1_tt \n");
  141. echo("// ---- $userID 是否能翻译 组织 zhang3 的译文 chinese-02+article-01?");
  142. $r1_tt1= UserCanTranslateTranslation($userID, "zhang3", "chinese-02", "article-01", $e);
  143. echo(" $r1_tt1 \n");
  144. echo("\n");
  145. }
  146. function CreateOrg($orgID ,$userID , $e ) {
  147. // 将该用户设置为组织机构的管理员
  148. $e->AddNamedGroupingPolicy(GroupRole, IDPrefixUser.$userID, RoleOrgAdmin, IDPrefixOrg.$orgID);
  149. // 添加 admin 资源操作权限
  150. $e->AddNamedPolicy("p", RoleOrgAdmin, IDPrefixOrg.$orgID, ResArticle, ".*");
  151. $e->AddNamedPolicy("p", RoleOrgAdmin, IDPrefixOrg.$orgID, ResChannel, ".*");
  152. $e->AddNamedPolicy("p", RoleOrgAdmin, IDPrefixOrg.$orgID, ResTranslation, ".*");
  153. // 添加 member 资源操作权限
  154. $e->AddNamedPolicy("p", RoleOrgMember, IDPrefixOrg.$orgID, ResArticle, PermRead);
  155. $e->AddNamedPolicy("p", RoleOrgMember, IDPrefixOrg.$orgID, ResChannel, PermRead);
  156. $e->AddNamedPolicy("p", RoleOrgMember, IDPrefixOrg.$orgID, ResTranslation, PermRead);
  157. }
  158. function AddOrgMemberToGroup($orgID, $groupID, $userID, $e ){
  159. // g, li4, translator, org1
  160. $e->AddNamedGroupingPolicy(GroupRole, IDPrefixUser . $userID, IDPrefixOrgGroup.$groupID, IDPrefixOrg.$orgID);
  161. }
  162. /*
  163. * 「为组织的group添加新成员」
  164. * 加入 group 分组,拥有组织资源读取权限
  165. */
  166. function AddOrgGroup($orgID,$groupId, $userID, $e ){
  167. $e->AddNamedGroupingPolicy(GroupRole, IDPrefixUser . $userID, $groupId, IDPrefixOrg . $orgID);
  168. }
  169. /*
  170. * 「为组织添加新成员」
  171. * 加入 member 分组,拥有组织资源读取权限
  172. */
  173. function AddOrgMember($orgID, $userID, $e ){
  174. $e->AddNamedGroupingPolicy(GroupRole, IDPrefixUser . $userID, RoleOrgMember, IDPrefixOrg . $orgID);
  175. }
  176. /*
  177. * 「为组织添加新管理员」
  178. * 加入 admin 分组,拥有组织资源一切权限 除了转让
  179. */
  180. function AddOrgAdmin($orgID, $userID, $e ){
  181. $e->AddNamedGroupingPolicy(GroupRole, IDPrefixUser . $userID, RoleOrgAdmin, IDPrefixOrg . $orgID);
  182. }
  183. function CreateChannel($channelID, $orgID, $e ){
  184. // 将该 Channel 资源放入本组织的 channel 分组
  185. $e->AddNamedGroupingPolicy(GroupRes, IDPrefixChannel . $channelID, ResChannel, IDPrefixOrg . $orgID);
  186. }
  187. /*
  188. * 「为版本风格添加只读用户」,也即是「分享版本风格」-「查看者」
  189. * 操作之后,该用户可以访问此版本风格下的所有译文
  190. */
  191. function AddChannelReader($channelID, $orgID, $userID, $e ){
  192. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermRead);
  193. }
  194. function AddChannelReader1($channelID, $orgID, $RoleID, $e ){
  195. $e->AddNamedPolicy("p", $RoleID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermRead);
  196. }
  197. function AddChannelReaderGroup($channelID, $orgID, $groupID, $e ){
  198. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermRead);
  199. }
  200. /*
  201. * 「为版本风格添加翻译用户」,也即是「分享版本风格」-「编辑者」
  202. * 操作之后,该用户可以编辑此版本风格下的所有译文
  203. * 注意:该权限并不能编辑「版本风格」本身
  204. */
  205. function AddChannelTranslator($channelID, $orgID, $userID, $e ){
  206. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermTrans);
  207. }
  208. function AddChannelTranslatorGroup($channelID, $orgID, $groupID, $e ){
  209. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermTrans);
  210. }
  211. /*
  212. * 「为版本风格添加编辑用户」
  213. * 操作之后,该用户可以编辑此版本风格本身,比如:
  214. * 分享此版本风格、修改版本风格的描述等等
  215. * //TODO: 此功能是否需要?
  216. */
  217. function AddChannelWriter($channelID, $orgID, $userID, $e ) {
  218. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermWrite);
  219. //return "", errors.New("Do we realy need this functiontion?")
  220. }
  221. function AddChannelWriterGroup($channelID, $orgID, $groupID, $e ) {
  222. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermWrite);
  223. //return "", errors.New("Do we realy need this functiontion?")
  224. }
  225. function CreateArticle($articleID, $orgID, $e ){
  226. // 将该 Article 资源放入本组织的 article 分组
  227. $e->AddNamedGroupingPolicy("g2", IDPrefixArticle.$articleID, ResArticle, IDPrefixOrg . $orgID);
  228. }
  229. /*
  230. * 「为文章添加只读用户」,也即是「分享文章」-「查看者」
  231. * 操作之后,该用户可以访问此文章模板,
  232. * 对于能否查看对应译文,需要由对应 channel 的权限来判定
  233. */
  234. function AddArticleReader($articleID, $orgID, $userID, $e ){
  235. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixArticle.$articleID, PermRead);
  236. }
  237. function AddArticleReaderGroup($articleID, $orgID, $groupID, $e ){
  238. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixArticle.$articleID, PermRead);
  239. }
  240. /*
  241. * 「为文章添加编辑用户」,也即是「分享文章」-「编辑者」
  242. * 操作之后,该用户可以编辑此文章模板,
  243. * 对于能否查看、或修改对应译文,需要由对应 channel 的权限来判定
  244. */
  245. function AddArticleWriter($articleID, $orgID, $userID, $e ){
  246. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixArticle.$articleID, PermWrite);
  247. }
  248. function AddArticleWriterGroup($articleID, $orgID,$groupID, $e ){
  249. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixArticle.$articleID, PermWrite);
  250. }
  251. /*
  252. * 创建译文 = 版本风格 + 文章模板
  253. * 所谓 “译文是由 版本风格 和 文章模板 组成”,这是一个幻象。
  254. * 当我们参与翻译的时候,最终的翻译结果是对应的经文句子编号,如:{{125-2347-2-14}},
  255. * 而不是 “翻译了这篇文章”,也就是说当前的翻译结果,可以在任何引用了该句子编号的文章里找到。
  256. *
  257. * 所以,与其说是 “创建了译文”,不如说是:
  258. * “通过在当前「文章模板」下基于某「版本风格」翻译该「句子编号」,
  259. * 导致该「文章模板」与该「版本风格」发生了「我曾经在该文章下使用该风格翻译了某句话」的关系”
  260. *
  261. * 那么,我们为什么要创建还要单独译文呢?
  262. * 1. 将「版本风格」与「文章模板」绑定,方便译者查找自己未完成的工作
  263. * 2. 单独创建的译文,可以单独分享,而不用分享整个「版本风格」加上单独分享「文章模板」
  264. * 2.1 如果单独分享了「版本风格」,那么该用户使用该风格翻译的其他「句子」也会被同时分享
  265. * 2.2 如果单独分享「文章模板」以及「版本风格」,两者并没有发生关联,
  266. * 那么被分享者也难以找到如此配对的资源
  267. *
  268. * 那么,如何判断当前访问的资源,是「文章对应的译文」还是「圣典对应的译文」呢?
  269. * 1. app/article?... 对应的资源便是「文章,以及对应的译文」
  270. * 2. app/reader?... 对应的资源便是「圣典,以及对应的译文」
  271. *
  272. * 那么,我们如何判断他们的权限呢?
  273. * 0. 如果以上两个资源都没有传递 channel 参数,则表示「不关特别联任何译文」
  274. * 则不需要做任何权限判断(可选择公开的版本风格显示译文)
  275. * 1. app/article?id=01&channel=01... article - 01 关联了 channel - 01
  276. * 1.1 首先该用户是否具有 channel - 01 和 article - 01 组成的译文的权限,如果有则授予
  277. 这样以来,用户可以单独分享「译文」,而不必分享「版本风格」+「文章模板」了,
  278. 同时也满足了「仅仅希望与其他同学一起编辑某一篇文章」的需求
  279. * 1.2 判断该用户是否拥有 article - 01 的权限,如果有,则进行 1.3 判断
  280. * 1.3 判断该用户是否具有 channel - 01 的权限,如果有,则授予,否则即没有权限
  281. * 2. app/reader?channel=01... 关联了 channel - 01
  282. * 2.1 判断该用户是否拥有 channel - 01 的权限即可
  283. *
  284. * 以上方法对于「写入译文」时的权限判断,同理可推得。推不得的话,我们讨论讨论。
  285. */
  286. function CreateTranslation($channelID, $articleID, $orgID, $e ){
  287. // 将该 Translation 资源放入本组织的 Translation 分组
  288. // ID 由 channelID + articleID 构成
  289. $e->AddNamedGroupingPolicy("g2", IDPrefixTranslation . $channelID."+".$articleID, ResTranslation, $orgID);
  290. }
  291. /*
  292. * 「为译文添加只读用户」,也即是仅分享「某版本风格」对应的「某篇文章模板」
  293. * 操作之后,该用户可以访问此译文,但
  294. * 并不能:单独访问该「文章模板」,或,访问该「版本风格」对应的其他译文,
  295. * 以上两种权限不在此处授予。
  296. */
  297. function AddTranslationReader($channelID, $articleID, $orgID, $userID, $e ){
  298. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixTranslation . $channelID."+".$articleID, PermRead);
  299. }
  300. function AddTranslationReaderGroup($channelID, $articleID, $orgID, $groupID, $e ){
  301. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixTranslation . $channelID."+".$articleID, PermRead);
  302. }
  303. /*
  304. * 「为译文添加翻译用户」,也即是仅分享「某版本风格」对应的「某篇文章模板」,并允许翻译
  305. * 操作之后,该用户可以访问、修改此译文,但
  306. * 并不能:单独访问该「文章模板」,或,访问或修改该「版本风格」对应的其他译文,
  307. * 以上两种权限不在此处授予。
  308. */
  309. function AddTranslationTranslator($channelID, $articleID, $orgID, $userID, $e ){
  310. $e->AddNamedPolicy("p", IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixTranslation . $channelID."+".$articleID, PermTrans);
  311. }
  312. function AddTranslationTranslatorGroup($channelID, $articleID, $orgID, $groupID, $e ){
  313. $e->AddNamedPolicy("p", IDPrefixOrgGroup . $groupID, IDPrefixOrg . $orgID, IDPrefixTranslation . $channelID."+".$articleID, PermTrans);
  314. }
  315. /*
  316. * //TODO 是否有这个需求?
  317. * 好像不需要将译文的修改权限分享出去,用户需要的是翻译。
  318. * 即便分享出去,能做什么呢?解除绑定?
  319. */
  320. function AddTranslationWriter($channelID, $articleID, $orgID, $userID, $e ) {
  321. //return "", errors.New("Do we realy need this functiontion?")
  322. }
  323. function AddTranslationWriterGroup($channelID, $articleID, $orgID, $groupID, $e ) {
  324. //return "", errors.New("Do we realy need this functiontion?")
  325. }
  326. function UserCanReadArticle($userID, $orgID, $articleID, $e ) {
  327. return $e->Enforce(IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixArticle.$articleID, PermRead.PermWrite);
  328. }
  329. function UserCanReadChannel($userID, $orgID, $channelID, $e ) {
  330. return $e->Enforce(IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermRead.PermWrite.PermTrans);
  331. }
  332. function UserCanWriteChannel($userID, $orgID, $channelID, $e ) {
  333. return $e->Enforce(IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermWrite);
  334. }
  335. function UserCanTranslateChannel($userID, $orgID, $channelID, $e ) {
  336. return $e->Enforce(IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixChannel . $channelID, PermTrans);
  337. }
  338. function UserCanReadTranslation($userID, $orgID, $channelID, $articleID, $e ) {
  339. // 先基于译文判断
  340. $r = $e->Enforce(IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixTranslation . $channelID."+".$articleID, PermRead.PermTrans);
  341. if (!$r) {
  342. // 再基于 版本风格 和 文章模板联合判断
  343. $r_channel = UserCanReadChannel($userID, $orgID, $channelID, $e);
  344. $r_article = UserCanReadArticle($userID, $orgID, $articleID, $e);
  345. if ($r_channel && $r_article) {
  346. return true;
  347. }
  348. }
  349. return $r;
  350. }
  351. function UserCanTranslateTranslation($userID, $orgID, $channelID, $articleID, $e ) {
  352. // 先基于译文判断
  353. $r = $e->Enforce(IDPrefixUser . $userID, IDPrefixOrg . $orgID, IDPrefixTranslation . $channelID."+".$articleID, PermTrans);
  354. if (!$r) {
  355. // 再基于 版本风格 和 文章模板联合判断
  356. $r_channel = UserCanTranslateChannel($userID, $orgID, $channelID, $e);
  357. $r_article = UserCanReadArticle($userID, $orgID, $articleID, $e);
  358. if ($r_channel && $r_article) {
  359. return true;
  360. }
  361. }
  362. return $r;
  363. }