@shaxpir/duiduidui-models 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +1 -0
  2. package/decs.d.ts +87 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.js +20 -0
  5. package/dist/models/OutboundMessage.d.ts +18 -0
  6. package/dist/models/OutboundMessage.js +25 -0
  7. package/dist/models/content/Activity.d.ts +10 -0
  8. package/dist/models/content/Activity.js +2 -0
  9. package/dist/models/content/ArrayView.d.ts +26 -0
  10. package/dist/models/content/ArrayView.js +174 -0
  11. package/dist/models/content/Billing.d.ts +144 -0
  12. package/dist/models/content/Billing.js +418 -0
  13. package/dist/models/content/Book.d.ts +77 -0
  14. package/dist/models/content/Book.js +407 -0
  15. package/dist/models/content/Category.d.ts +16 -0
  16. package/dist/models/content/Category.js +20 -0
  17. package/dist/models/content/Checkpointable.d.ts +21 -0
  18. package/dist/models/content/Checkpointable.js +156 -0
  19. package/dist/models/content/Comment.d.ts +19 -0
  20. package/dist/models/content/Comment.js +53 -0
  21. package/dist/models/content/ConceptArt.d.ts +31 -0
  22. package/dist/models/content/ConceptArt.js +84 -0
  23. package/dist/models/content/Content.d.ts +52 -0
  24. package/dist/models/content/Content.js +61 -0
  25. package/dist/models/content/ContentKind.d.ts +10 -0
  26. package/dist/models/content/ContentKind.js +16 -0
  27. package/dist/models/content/Context.d.ts +28 -0
  28. package/dist/models/content/Context.js +162 -0
  29. package/dist/models/content/DevEnv.d.ts +5 -0
  30. package/dist/models/content/DevEnv.js +9 -0
  31. package/dist/models/content/Device.d.ts +24 -0
  32. package/dist/models/content/Device.js +62 -0
  33. package/dist/models/content/Dictionary.d.ts +31 -0
  34. package/dist/models/content/Dictionary.js +5 -0
  35. package/dist/models/content/DictionaryEntry.d.ts +20 -0
  36. package/dist/models/content/DictionaryEntry.js +2 -0
  37. package/dist/models/content/ElasticModel.d.ts +149 -0
  38. package/dist/models/content/ElasticModel.js +179 -0
  39. package/dist/models/content/Environment.d.ts +61 -0
  40. package/dist/models/content/Environment.js +124 -0
  41. package/dist/models/content/ExportOptions.d.ts +64 -0
  42. package/dist/models/content/ExportOptions.js +213 -0
  43. package/dist/models/content/Folder.d.ts +16 -0
  44. package/dist/models/content/Folder.js +33 -0
  45. package/dist/models/content/Fragment.d.ts +54 -0
  46. package/dist/models/content/Fragment.js +181 -0
  47. package/dist/models/content/GeoLocation.d.ts +4 -0
  48. package/dist/models/content/GeoLocation.js +2 -0
  49. package/dist/models/content/Hanzi.d.ts +21 -0
  50. package/dist/models/content/Hanzi.js +2 -0
  51. package/dist/models/content/HighlightRule.d.ts +9 -0
  52. package/dist/models/content/HighlightRule.js +2 -0
  53. package/dist/models/content/Manifest.d.ts +42 -0
  54. package/dist/models/content/Manifest.js +114 -0
  55. package/dist/models/content/Media.d.ts +32 -0
  56. package/dist/models/content/Media.js +98 -0
  57. package/dist/models/content/Metric.d.ts +46 -0
  58. package/dist/models/content/Metric.js +183 -0
  59. package/dist/models/content/Migration.d.ts +68 -0
  60. package/dist/models/content/Migration.js +155 -0
  61. package/dist/models/content/Model.d.ts +45 -0
  62. package/dist/models/content/Model.js +280 -0
  63. package/dist/models/content/Permissions.d.ts +7 -0
  64. package/dist/models/content/Permissions.js +20 -0
  65. package/dist/models/content/Phrase.d.ts +8 -0
  66. package/dist/models/content/Phrase.js +2 -0
  67. package/dist/models/content/Placeholder.d.ts +8 -0
  68. package/dist/models/content/Placeholder.js +36 -0
  69. package/dist/models/content/Profile.d.ts +30 -0
  70. package/dist/models/content/Profile.js +95 -0
  71. package/dist/models/content/RichText.d.ts +58 -0
  72. package/dist/models/content/RichText.js +79 -0
  73. package/dist/models/content/Session.d.ts +39 -0
  74. package/dist/models/content/Session.js +173 -0
  75. package/dist/models/content/Speech.d.ts +67 -0
  76. package/dist/models/content/Speech.js +97 -0
  77. package/dist/models/content/Stub.d.ts +24 -0
  78. package/dist/models/content/Stub.js +179 -0
  79. package/dist/models/content/Time.d.ts +56 -0
  80. package/dist/models/content/Time.js +295 -0
  81. package/dist/models/content/User.d.ts +36 -0
  82. package/dist/models/content/User.js +95 -0
  83. package/dist/models/content/Workspace.d.ts +71 -0
  84. package/dist/models/content/Workspace.js +237 -0
  85. package/dist/models/content/index.d.ts +36 -0
  86. package/dist/models/content/index.js +53 -0
  87. package/dist/models/index.d.ts +4 -0
  88. package/dist/models/index.js +21 -0
  89. package/dist/models/legacy/LegacyBodyFormat.d.ts +9 -0
  90. package/dist/models/legacy/LegacyBodyFormat.js +2 -0
  91. package/dist/models/legacy/LegacyComment.d.ts +12 -0
  92. package/dist/models/legacy/LegacyComment.js +2 -0
  93. package/dist/models/legacy/LegacyContent.d.ts +53 -0
  94. package/dist/models/legacy/LegacyContent.js +55 -0
  95. package/dist/models/legacy/LegacyConversion.d.ts +55 -0
  96. package/dist/models/legacy/LegacyConversion.js +401 -0
  97. package/dist/models/legacy/LegacyFragment.d.ts +21 -0
  98. package/dist/models/legacy/LegacyFragment.js +2 -0
  99. package/dist/models/legacy/LegacyLocator.d.ts +8 -0
  100. package/dist/models/legacy/LegacyLocator.js +31 -0
  101. package/dist/models/legacy/LegacyOutboundMessage.d.ts +16 -0
  102. package/dist/models/legacy/LegacyOutboundMessage.js +13 -0
  103. package/dist/models/legacy/LegacyPicture.d.ts +14 -0
  104. package/dist/models/legacy/LegacyPicture.js +2 -0
  105. package/dist/models/legacy/LegacyProfile.d.ts +9 -0
  106. package/dist/models/legacy/LegacyProfile.js +2 -0
  107. package/dist/models/legacy/LegacySession.d.ts +41 -0
  108. package/dist/models/legacy/LegacySession.js +35 -0
  109. package/dist/models/legacy/LegacyStory.d.ts +23 -0
  110. package/dist/models/legacy/LegacyStory.js +2 -0
  111. package/dist/models/legacy/LegacyStub.d.ts +15 -0
  112. package/dist/models/legacy/LegacyStub.js +2 -0
  113. package/dist/models/legacy/LegacyTransaction.d.ts +14 -0
  114. package/dist/models/legacy/LegacyTransaction.js +49 -0
  115. package/dist/models/legacy/LegacyUser.d.ts +28 -0
  116. package/dist/models/legacy/LegacyUser.js +32 -0
  117. package/dist/models/legacy/LegacyWorkspace.d.ts +23 -0
  118. package/dist/models/legacy/LegacyWorkspace.js +6 -0
  119. package/dist/models/legacy/index.d.ts +15 -0
  120. package/dist/models/legacy/index.js +32 -0
  121. package/dist/models/markup/BodyFormat.d.ts +14 -0
  122. package/dist/models/markup/BodyFormat.js +190 -0
  123. package/dist/models/markup/ChangeModel.d.ts +22 -0
  124. package/dist/models/markup/ChangeModel.js +107 -0
  125. package/dist/models/markup/DeltaOps.d.ts +5 -0
  126. package/dist/models/markup/DeltaOps.js +74 -0
  127. package/dist/models/markup/HtmlMarkup.d.ts +4 -0
  128. package/dist/models/markup/HtmlMarkup.js +21 -0
  129. package/dist/models/markup/Operation.d.ts +32 -0
  130. package/dist/models/markup/Operation.js +194 -0
  131. package/dist/models/markup/TextEditOps.d.ts +9 -0
  132. package/dist/models/markup/TextEditOps.js +50 -0
  133. package/dist/models/markup/index.d.ts +6 -0
  134. package/dist/models/markup/index.js +23 -0
  135. package/dist/repo/ConnectionListener.d.ts +9 -0
  136. package/dist/repo/ConnectionListener.js +21 -0
  137. package/dist/repo/PermissiveJson1.d.ts +58 -0
  138. package/dist/repo/PermissiveJson1.js +39 -0
  139. package/dist/repo/ShareSync.d.ts +60 -0
  140. package/dist/repo/ShareSync.js +348 -0
  141. package/dist/repo/index.d.ts +3 -0
  142. package/dist/repo/index.js +20 -0
  143. package/dist/util/Async.d.ts +8 -0
  144. package/dist/util/Async.js +18 -0
  145. package/dist/util/Base62.d.ts +6 -0
  146. package/dist/util/Base62.js +47 -0
  147. package/dist/util/BinarySearch.d.ts +7 -0
  148. package/dist/util/BinarySearch.js +46 -0
  149. package/dist/util/CachingHasher.d.ts +8 -0
  150. package/dist/util/CachingHasher.js +41 -0
  151. package/dist/util/Color.d.ts +32 -0
  152. package/dist/util/Color.js +204 -0
  153. package/dist/util/Dispatch.d.ts +15 -0
  154. package/dist/util/Dispatch.js +79 -0
  155. package/dist/util/EditDistance.d.ts +13 -0
  156. package/dist/util/EditDistance.js +184 -0
  157. package/dist/util/Encryption.d.ts +5 -0
  158. package/dist/util/Encryption.js +2 -0
  159. package/dist/util/Logging.d.ts +108 -0
  160. package/dist/util/Logging.js +412 -0
  161. package/dist/util/NumberFormat.d.ts +14 -0
  162. package/dist/util/NumberFormat.js +224 -0
  163. package/dist/util/Struct.d.ts +4 -0
  164. package/dist/util/Struct.js +15 -0
  165. package/dist/util/Template.d.ts +16 -0
  166. package/dist/util/Template.js +128 -0
  167. package/dist/util/Text.d.ts +45 -0
  168. package/dist/util/Text.js +243 -0
  169. package/dist/util/Tuples.d.ts +9 -0
  170. package/dist/util/Tuples.js +135 -0
  171. package/dist/util/Validate.d.ts +4 -0
  172. package/dist/util/Validate.js +11 -0
  173. package/dist/util/Vocabulary.d.ts +3 -0
  174. package/dist/util/Vocabulary.js +35 -0
  175. package/dist/util/index.d.ts +16 -0
  176. package/dist/util/index.js +33 -0
  177. package/lib/models/content/ArrayView.ts +203 -0
  178. package/lib/models/content/Billing.ts +558 -0
  179. package/lib/models/content/Content.ts +110 -0
  180. package/lib/models/content/ContentKind.ts +14 -0
  181. package/lib/models/content/DevEnv.ts +5 -0
  182. package/lib/models/content/Device.ts +86 -0
  183. package/lib/models/content/DictionaryEntry.ts +22 -0
  184. package/lib/models/content/GeoLocation.ts +4 -0
  185. package/lib/models/content/Hanzi.ts +25 -0
  186. package/lib/models/content/Manifest.ts +162 -0
  187. package/lib/models/content/Media.ts +126 -0
  188. package/lib/models/content/Model.ts +327 -0
  189. package/lib/models/content/Permissions.ts +21 -0
  190. package/lib/models/content/Phrase.ts +10 -0
  191. package/lib/models/content/Profile.ts +119 -0
  192. package/lib/models/content/Time.ts +328 -0
  193. package/lib/models/content/User.ts +130 -0
  194. package/lib/models/markup/ChangeModel.ts +95 -0
  195. package/lib/models/markup/DeltaOps.ts +71 -0
  196. package/lib/models/markup/Operation.ts +215 -0
  197. package/lib/models/markup/TextEditOps.ts +50 -0
  198. package/lib/repo/ConnectionListener.ts +25 -0
  199. package/lib/repo/PermissiveJson1.ts +14 -0
  200. package/lib/repo/ShareSync.ts +390 -0
  201. package/lib/util/Base62.ts +47 -0
  202. package/lib/util/CachingHasher.ts +38 -0
  203. package/lib/util/Dispatch.ts +92 -0
  204. package/lib/util/Encryption.ts +5 -0
  205. package/lib/util/Logging.ts +568 -0
  206. package/lib/util/NumberFormat.ts +194 -0
  207. package/lib/util/Struct.ts +14 -0
  208. package/lib/util/Tuples.ts +131 -0
  209. package/package.json +41 -0
  210. package/tsconfig.json +25 -0
  211. package/tslint.json +46 -0
@@ -0,0 +1,327 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { ShareSync } from '../../repo/ShareSync';
3
+ import { Struct } from '../../util/Struct';
4
+ import { Dispatch, DispatchTopic } from '../../util/Dispatch';
5
+ import { ChangeModel } from '../markup/ChangeModel';
6
+ import { ContentId, ContentMeta, ContentRef } from './Content';
7
+ import { ContentKind } from './ContentKind';
8
+ import { ManifestBody } from './Manifest';
9
+ import { PermissionType } from './Permissions';
10
+ import { CompactDateTime } from './Time';
11
+
12
+ export abstract class Model {
13
+
14
+ public static CHANGED:DispatchTopic = "MODEL_CHANGED";
15
+
16
+ public doc:Doc;
17
+ private _isDisposed:boolean;
18
+
19
+ // The "forbidden" flag is set when the user does not have permission to read or write the model.
20
+ // We set this flag during fetch, subscribe, and ensureData operations, if the server responds with
21
+ // 'forbidden'. Rather than throwing an exception or emitting an error event, or calling an error
22
+ // callback, this technique allows the nornal flow of logic to proceed, and the model can be checked
23
+ // for the forbidden flag at any time. Callers can (for example) display a 404 page, or a "forbidden"
24
+ // message, or simply ignore the model, depending on the context, rather than having to handle errors.
25
+ // But it should be forewarned that any BatchOperation that is attempted on a forbidden model will
26
+ // throw an exception, because the model data is not allowed to be read or written by this user.
27
+ private _isForbidden:boolean;
28
+
29
+ private _shouldPerformModelChangeAnalysis:boolean;
30
+ private _dataBeforeOpBatch:any = null;
31
+ private _clearEventListeners:() => void = null;
32
+
33
+ protected shareSync:ShareSync;
34
+ private _subscribingPromise:Promise<Model> = null;
35
+ private _acquireCount:number = 0;
36
+
37
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
38
+ const model = this;
39
+ model.doc = doc;
40
+ model.shareSync = shareSync;
41
+ model._isDisposed = false;
42
+ model._isForbidden = false;
43
+ // When a model is newly created, or retrieved via a "fetch query", treat that as an implicit
44
+ // call to "acquire", since the SharedDB doc object will eventually need to be disposed.
45
+ if (shouldAcquire) {
46
+ this._acquireCount++;
47
+ }
48
+ const onBeforeOpBatch = function (op:any, source:any) {
49
+ if (model._shouldPerformModelChangeAnalysis) {
50
+ model._dataBeforeOpBatch = Struct.clone(model.doc.data);
51
+ }
52
+ };
53
+ const onOpBatch = function (op:any, source:any) {
54
+ if (model._shouldPerformModelChangeAnalysis) {
55
+ // Use tuple-arrays to perform a diff...
56
+ const dataAfterOpBatch = Struct.clone(model.doc.data);
57
+ let changeItems = ChangeModel.between(model._dataBeforeOpBatch, dataAfterOpBatch);
58
+ model._dataBeforeOpBatch = null;
59
+ // FROM https://share.github.io/sharedb/api/doc ...
60
+ // The 'source' value will be false for remote ops received from other clients, or will be truthy
61
+ // for ops submitted from this doc instance. For local ops, it will be the value of source supplied
62
+ // to submitOp, or true if no value was supplied
63
+ const isLocalChange = !!source;
64
+ const change = {
65
+ local: isLocalChange,
66
+ model: model,
67
+ items: changeItems,
68
+ op: op
69
+ };
70
+ Dispatch.publish(Model.CHANGED, change);
71
+ }
72
+ };
73
+ model.doc.on('before op batch', onBeforeOpBatch);
74
+ model.doc.on('op batch', onOpBatch);
75
+ this._clearEventListeners = function () {
76
+ if (model.doc && model.doc.off) {
77
+ model.doc.off('before op batch', onBeforeOpBatch);
78
+ model.doc.off('op batch', onOpBatch);
79
+ }
80
+ };
81
+ }
82
+
83
+ public get kind():ContentKind {
84
+ return this.doc.collection as ContentKind;
85
+ }
86
+
87
+ public get ref():ContentRef {
88
+ return this.doc.id as ContentRef;
89
+ }
90
+
91
+ public get compoundKey():string {
92
+ return `${this.doc.collection}/${this.doc.id}`;
93
+ }
94
+
95
+ public exists():boolean {
96
+ return (!!this.doc.type) && !this.isForbidden;
97
+ }
98
+
99
+ public get isForbidden():boolean {
100
+ return this._isForbidden;
101
+ }
102
+
103
+ public get isSubscribed():boolean {
104
+ return this.doc.subscribed;
105
+ }
106
+
107
+ public get acquireCount():number {
108
+ return this._acquireCount;
109
+ }
110
+
111
+ public async acquire(minVersion?:CompactDateTime):Promise<Model> {
112
+ this._acquireCount++;
113
+ if (minVersion) {
114
+ this.log(`ACQUIRE: (${this.compoundKey}) at minVersion '${minVersion}'; acquireCount = ${this.acquireCount}`);
115
+ return this.ensureRecentData(minVersion);
116
+ } else {
117
+ this.log(`ACQUIRE: (${this.compoundKey}); acquireCount = ${this.acquireCount}`);
118
+ return this.ensureData();
119
+ }
120
+ }
121
+
122
+ public release():void {
123
+ this._acquireCount--;
124
+ this.log(`RELEASE: (${this.compoundKey}); acquireCount = ${this.acquireCount}`);
125
+ if (this._acquireCount <= 0) {
126
+ this._acquireCount = 0;
127
+ this.dispose()
128
+ }
129
+ }
130
+
131
+ public dispose():void {
132
+ const model = this;
133
+ model._clearEventListeners();
134
+ model.shareSync.dispose(model);
135
+ }
136
+
137
+ public disposeAndDestroy():void {
138
+ this.log(`DISPOSE (AND DESTROY): (${this.compoundKey})`);
139
+ this._isDisposed = true;
140
+ this.doc.destroy();
141
+ }
142
+
143
+ public disposeAndUnsubscribe():void {
144
+ this.log(`DISPOSE (AND UNSUBSCRIBE): (${this.compoundKey})`);
145
+ this._isDisposed = true;
146
+ if (this.isSubscribed) {
147
+ this.unsubscribe();
148
+ }
149
+ }
150
+
151
+ public async fetch():Promise<Model> {
152
+ this.log(`FETCH: (${this.compoundKey})`);
153
+ const model = this;
154
+ return new Promise((resolve, reject) => {
155
+ model.doc.fetch((fetchErr:any) => {
156
+ if (fetchErr) {
157
+ if (fetchErr.message == 'forbidden') {
158
+ model._isForbidden = true;
159
+ resolve(model);
160
+ } else {
161
+ const message = `error fetching model ${model.compoundKey}: ${fetchErr}`;
162
+ console.log(message);
163
+ model.release();
164
+ reject(message);
165
+ }
166
+ } else {
167
+ model.shareSync.isDebug() && console.log(`fetched model ${model.compoundKey}`);
168
+ resolve(model);
169
+ }
170
+ });
171
+ });
172
+ }
173
+
174
+ public async subscribe():Promise<Model> {
175
+ this.log(`SUBSCRIBE: (${this.compoundKey})`);
176
+ if (this._subscribingPromise != null) {
177
+ return this._subscribingPromise;
178
+ }
179
+ if (this.isSubscribed) {
180
+ return this;
181
+ }
182
+ const model = this;
183
+ this._subscribingPromise = new Promise((resolve, reject) => {
184
+ this.doc.subscribe((subscribeErr:any) => {
185
+ model._subscribingPromise = null;
186
+ if (subscribeErr) {
187
+ if (subscribeErr.message == 'forbidden') {
188
+ model._isForbidden = true;
189
+ resolve(model);
190
+ } else {
191
+ const message = `error subscribing to Model ${model.compoundKey}: ${subscribeErr}`;
192
+ console.log(message);
193
+ reject(message);
194
+ }
195
+ } else {
196
+ model.shareSync.isDebug() && console.log(`subscribed to Model ${model.compoundKey}`);
197
+ resolve(model);
198
+ }
199
+ });
200
+ });
201
+ return this._subscribingPromise;
202
+ }
203
+
204
+ public async unsubscribe():Promise<Model> {
205
+ this.log(`UNSUBSCRIBE: (${this.compoundKey})`);
206
+ if (this._subscribingPromise != null) {
207
+ await this._subscribingPromise;
208
+ }
209
+ if (!this.isSubscribed) {
210
+ return this;
211
+ }
212
+ const model = this;
213
+ return new Promise((resolve, reject) => {
214
+ this.doc.unsubscribe((unsubscribeErr:any) => {
215
+ if (unsubscribeErr) {
216
+ const message = `error unsubscribing from Model ${model.compoundKey}: ${unsubscribeErr}`;
217
+ console.log(message);
218
+ reject(message);
219
+ } else {
220
+ model.shareSync.isDebug() && console.log(`subscribed to Model ${model.compoundKey}`);
221
+ resolve(model);
222
+ }
223
+ });
224
+ });
225
+ }
226
+
227
+ public isDisposed():boolean {
228
+ return this._isDisposed;
229
+ }
230
+
231
+ public checkDisposed(source:string):void {
232
+ if (this._isDisposed) {
233
+ console.error(`*** MODEL ALREDY DISPOSED: (${this.compoundKey}); source = ${source}`);
234
+ }
235
+ }
236
+
237
+ public async ensureData():Promise<Model> {
238
+ const model = this;
239
+ return new Promise((resolve, reject) => {
240
+ this.doc.ensureDocHasData((ensureErr:any) => {
241
+ if (ensureErr) {
242
+ if (ensureErr.message == 'forbidden') {
243
+ model._isForbidden = true;
244
+ resolve(model);
245
+ } else {
246
+ const message = `error ensuring doc has data in Model ${model.compoundKey}: ${ensureErr}`;
247
+ console.log(message);
248
+ reject(message);
249
+ }
250
+ } else {
251
+ model.shareSync.isDebug() && console.log(`ensured doc has data for Model ${model.compoundKey}`);
252
+ resolve(model);
253
+ }
254
+ });
255
+ });
256
+ }
257
+
258
+ public async ensureRecentData(minVersion:CompactDateTime):Promise<Model> {
259
+ const model = this;
260
+ return new Promise((resolve, reject) => {
261
+ this.doc.ensureDocHasRecentData(minVersion, (ensureErr:any) => {
262
+ if (ensureErr) {
263
+ if (ensureErr.message == 'forbidden') {
264
+ model._isForbidden = true;
265
+ resolve(model);
266
+ } else {
267
+ const message = `error ensuring doc has recent data in Model ${model.compoundKey} with minVersion ${minVersion}: ${ensureErr}`;
268
+ console.log(message);
269
+ reject(message);
270
+ }
271
+ } else {
272
+ model.shareSync.isDebug() && console.log(`ensured doc has recent data for Model ${model.compoundKey} with minVersion ${minVersion}`);
273
+ resolve(model);
274
+ }
275
+ });
276
+ });
277
+ }
278
+
279
+ public doesUserHavePermission(
280
+ userId:ContentId,
281
+ type:PermissionType
282
+ ):boolean {
283
+ this.checkDisposed("Model.doesUserHavePermission");
284
+ if (this.kind === ContentKind.MANIFEST) {
285
+ return Model.doesUserHaveManifestPermission(this.doc.data, userId, type);
286
+ } else {
287
+ // Content objects and checkpoints all have a PermissionsMeta
288
+ const meta = this.doc.data.meta as ContentMeta;
289
+ return Model.doesUserHaveContentPermission(this.kind, meta, userId, type);
290
+ }
291
+ }
292
+
293
+ public static doesUserHaveManifestPermission(
294
+ data:ManifestBody,
295
+ userId:ContentId,
296
+ type:PermissionType
297
+ ):boolean {
298
+ return data.meta.owner === userId;
299
+ }
300
+
301
+ public static doesUserHaveContentPermission(
302
+ kind:ContentKind,
303
+ meta:ContentMeta,
304
+ userId:ContentId,
305
+ type:PermissionType
306
+ ):boolean {
307
+
308
+ // Users are allowed to read any of their own content, but they are not allowed to write their User or Billing objects.
309
+ if (meta.owner === userId) {
310
+ if (type === PermissionType.READ) {
311
+ return true;
312
+ } else if (type === PermissionType.WRITE) {
313
+ return kind !== ContentKind.USER && kind !== ContentKind.BILLING;
314
+ }
315
+ }
316
+
317
+ // If all else fails, deny permission.
318
+ return false;
319
+ }
320
+
321
+ private log(message:string):void {
322
+ if (this.shareSync.isDebug()) {
323
+ console.log(message);
324
+ }
325
+ }
326
+
327
+ }
@@ -0,0 +1,21 @@
1
+ import { ContentId } from "./Content";
2
+
3
+ export enum PermissionType {
4
+ READ,
5
+ WRITE
6
+ }
7
+
8
+ export class PermissionModel {
9
+
10
+ public static shouldAllow(
11
+ grantedPermission:PermissionType,
12
+ necessaryPermission:PermissionType
13
+ ):boolean {
14
+ if (grantedPermission == necessaryPermission) {
15
+ return true;
16
+ } else if (grantedPermission == PermissionType.WRITE && necessaryPermission == PermissionType.READ) {
17
+ return true;
18
+ }
19
+ return false;
20
+ }
21
+ }
@@ -0,0 +1,10 @@
1
+ import { ContentBody, ContentMeta } from "./Content";
2
+ import { Definable, DictionaryEntry, Pronouncable } from "./DictionaryEntry";
3
+
4
+ export interface PhrasePayload extends DictionaryEntry, Pronouncable, Definable {
5
+ }
6
+
7
+ export interface PhraseBody extends ContentBody {
8
+ meta:ContentMeta;
9
+ payload:PhrasePayload;
10
+ }
@@ -0,0 +1,119 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { ShareSync, ShareSyncFactory } from '../../repo/ShareSync';
3
+ import { CachingHasher } from '../../util/CachingHasher';
4
+ import { BatchOperation } from '../markup/Operation';
5
+ import { Content, ContentBody, ContentId, ContentMeta, ContentRef } from "./Content";
6
+ import { ContentKind } from './ContentKind';
7
+ import { MediaCropping } from './Media';
8
+ import { MultiClock } from './Time';
9
+
10
+ export interface ProfilePayload {
11
+ username:string;
12
+ full_name:string;
13
+ avatar_media_ref:ContentId;
14
+ avatar_cropping:MediaCropping;
15
+ }
16
+
17
+ export interface ProfileBody extends ContentBody {
18
+ meta:ContentMeta;
19
+ payload:ProfilePayload;
20
+ }
21
+
22
+ export class Profile extends Content {
23
+
24
+ public static makeProfileId(userId:ContentId):ContentId {
25
+ return CachingHasher.makeMd5ContentId(userId + "-" + ContentKind.PROFILE);
26
+ }
27
+
28
+ public static create(userId:ContentId):Profile {
29
+ const now = MultiClock.now();
30
+ const profileId = Profile.makeProfileId(userId);
31
+ return ShareSyncFactory.get().createContent(
32
+ {
33
+ meta : {
34
+ ref : profileId,
35
+ is_head : true,
36
+ kind : ContentKind.PROFILE,
37
+ id : profileId,
38
+ owner : userId,
39
+ created_at : now,
40
+ updated_at : now
41
+ },
42
+ payload : {
43
+ username : null,
44
+ full_name : '',
45
+ avatar_media_ref : null as ContentRef,
46
+ avatar_cropping : null
47
+ }
48
+ }
49
+ ) as Profile;
50
+ }
51
+
52
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
53
+ super(doc, shouldAcquire, shareSync);
54
+ }
55
+
56
+ public get payload():ProfilePayload {
57
+ this.checkDisposed("Profile.payload");
58
+ return this.doc.data.payload as ProfilePayload;
59
+ }
60
+
61
+ public get username():string {
62
+ this.checkDisposed("Profile.username");
63
+ return this.payload.username;
64
+ }
65
+ public setUsername(value:string) {
66
+ this.checkDisposed("Profile.setUsername");
67
+ if (this.username != value) {
68
+ const batch = new BatchOperation(this);
69
+ batch.setPathValue([ 'payload', 'username' ] , value);
70
+ batch.commit();
71
+ }
72
+ }
73
+
74
+ public get fullName():string {
75
+ this.checkDisposed("Profile.fullName");
76
+ return this.payload.full_name;
77
+ }
78
+ public setFullName(value:string) {
79
+ this.checkDisposed("Profile.setFullName");
80
+ if (this.fullName !== value) {
81
+ const batch = new BatchOperation(this);
82
+ batch.editPathText([ 'payload', 'full_name' ] , value);
83
+ batch.commit();
84
+ }
85
+ }
86
+
87
+ public get avatarMediaRef():ContentId {
88
+ this.checkDisposed("Profile.avatarMediaRef");
89
+ return this.payload.avatar_media_ref;
90
+ }
91
+ public setAvatarMediaRef(value:ContentId) {
92
+ this.checkDisposed("Profile.setAvatarMediaRef");
93
+ if (this.avatarMediaRef !== value) {
94
+ const batch = new BatchOperation(this);
95
+ batch.setPathValue([ 'payload', 'avatar_media_ref' ] , value);
96
+ batch.commit();
97
+ }
98
+ }
99
+
100
+ public get avatarCropping():MediaCropping {
101
+ this.checkDisposed("Profile.avatarCropping");
102
+ return this.payload.avatar_cropping;
103
+ }
104
+ public setAvatarCropping(value:MediaCropping) {
105
+ this.checkDisposed("Profile.setAvatarCropping");
106
+ if (this.avatarCropping !== value) {
107
+ const batch = new BatchOperation(this);
108
+ batch.setPathValue([ 'payload', 'avatar_cropping' ] , value);
109
+ batch.commit();
110
+ }
111
+ }
112
+
113
+ public static async findByUsername(username:string):Promise<Profile[]> {
114
+ const shareSync = ShareSyncFactory.get();
115
+ return shareSync.findAndAcquire(
116
+ ContentKind.PROFILE, { "payload.username" : username }
117
+ ) as Promise<Profile[]>;
118
+ }
119
+ }