~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to lib/schemav79.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the OpenSSL
 
20
# library.
 
21
#
 
22
# You must obey the GNU General Public License in all respects for all of
 
23
# the code used other than OpenSSL. If you modify file(s) with this
 
24
# exception, you may extend this exception to your version of the file(s),
 
25
# but you are not obligated to do so. If you do not wish to do so, delete
 
26
# this exception statement from your version. If you delete this exception
 
27
# statement from all source files in the program, then also delete it here.
 
28
 
 
29
"""schemav79.py -- schema.py, frozen at version 79.
 
30
 
 
31
This is the magic version where we switched the database style.  We keep this
 
32
schema version around in order to create the tables needed to upgrade
 
33
databases from version 2.0 (and before).
 
34
 
 
35
The schema module is responsible for defining what data in the database
 
36
gets stored on disk.  
 
37
 
 
38
The goals of this modules are:
 
39
 
 
40
* Clearly defining which data from DDBObjects gets stored and which doesn't.
 
41
* Validating that all data we write can be read back in
 
42
* Making upgrades of the database schema as easy as possible
 
43
 
 
44
Module-level variables:
 
45
    objectSchemas -- Schemas to use with the current database.
 
46
    VERSION -- Current schema version.  If you change the schema you must bump
 
47
    this number and add a function in the databaseupgrade module.
 
48
 
 
49
Go to the bottom of this file for the current database schema.
 
50
"""
 
51
 
 
52
import datetime
 
53
import time
 
54
from types import NoneType
 
55
from miro.plat.utils import FilenameType
 
56
from miro.frontendstate import WidgetsFrontendState
 
57
 
 
58
class ValidationError(Exception):
 
59
    """Error thrown when we try to save invalid data."""
 
60
    pass
 
61
 
 
62
class ValidationWarning(Warning):
 
63
    """Warning issued when we try to restore invalid data."""
 
64
    pass
 
65
 
 
66
class SchemaItem(object):
 
67
    """SchemaItem represents a single attribute that gets stored on disk.
 
68
 
 
69
    SchemaItem is an abstract class.  Subclasses of SchemaItem such as
 
70
    SchemaAttr, SchemaList are used in actual object schemas.
 
71
 
 
72
    Member variables:
 
73
        noneOk -- specifies if None is a valid value for this attribute
 
74
    """
 
75
 
 
76
    def __init__(self, noneOk=False):
 
77
        self.noneOk = noneOk
 
78
 
 
79
    def validate(self, data):
 
80
        """Validate that data is a valid value for this SchemaItem.
 
81
 
 
82
        validate is "dumb" when it comes to container types like SchemaList,
 
83
        etc.  It only checks that the container is the right type, not its
 
84
        children.  This isn't a problem because saveObject() calls
 
85
        validate() recursively on all the data it saves, therefore validate
 
86
        doesn't have to recursively validate things.
 
87
        """
 
88
 
 
89
        if data is None:
 
90
            if not self.noneOk:
 
91
                raise ValidationError("None value is not allowed")
 
92
        return True
 
93
 
 
94
    def validateType(self, data, correctType):
 
95
        """Helper function that many subclasses use"""
 
96
        if data is not None and not isinstance(data, correctType):
 
97
            raise ValidationError("%r (type: %s) is not a %s" % 
 
98
                    (data, type(data), correctType))
 
99
 
 
100
    def validateTypes(self, data, possibleTypes):
 
101
        if data is None:
 
102
            return
 
103
        for t in possibleTypes:
 
104
            if isinstance(data, t):
 
105
                return
 
106
        raise ValidationError("%r (type: %s) is not any of: %s" % 
 
107
                (data, type(data), possibleTypes))
 
108
 
 
109
class SchemaSimpleItem(SchemaItem):
 
110
    """Base class for SchemaItems for simple python types."""
 
111
 
 
112
class SchemaBool(SchemaSimpleItem):
 
113
    def validate(self, data):
 
114
        super(SchemaSimpleItem, self).validate(data)
 
115
        self.validateType(data, bool)
 
116
 
 
117
class SchemaFloat(SchemaSimpleItem):
 
118
    def validate(self, data):
 
119
        super(SchemaSimpleItem, self).validate(data)
 
120
        self.validateType(data, float)
 
121
 
 
122
class SchemaString(SchemaSimpleItem):
 
123
    def validate(self, data):
 
124
        super(SchemaSimpleItem, self).validate(data)
 
125
        self.validateType(data, unicode)
 
126
 
 
127
class SchemaBinary(SchemaSimpleItem):
 
128
    def validate(self, data):
 
129
        super(SchemaSimpleItem, self).validate(data)
 
130
        self.validateType(data, str)
 
131
 
 
132
class SchemaFilename(SchemaSimpleItem):
 
133
    def validate(self, data):
 
134
        super(SchemaSimpleItem, self).validate(data)
 
135
        self.validateType(data, FilenameType)
 
136
 
 
137
class SchemaURL(SchemaSimpleItem):
 
138
    def validate(self, data):
 
139
        super(SchemaSimpleItem, self).validate(data)
 
140
        self.validateType(data, unicode)
 
141
        if data:
 
142
            try:
 
143
                data.encode('ascii')
 
144
            except UnicodeEncodeError:
 
145
                ValidationError(u"URL (%s) is not ASCII" % data)
 
146
 
 
147
class SchemaInt(SchemaSimpleItem):
 
148
    def validate(self, data):
 
149
        super(SchemaSimpleItem, self).validate(data)
 
150
        self.validateTypes(data, [int, long])
 
151
 
 
152
class SchemaDateTime(SchemaSimpleItem):
 
153
    def validate(self, data):
 
154
        super(SchemaSimpleItem, self).validate(data)
 
155
        self.validateType(data, datetime.datetime)
 
156
 
 
157
class SchemaTimeDelta(SchemaSimpleItem):
 
158
    def validate(self, data):
 
159
        super(SchemaSimpleItem, self).validate(data)
 
160
        self.validateType(data, datetime.timedelta)
 
161
 
 
162
class SchemaReprContainer(SchemaItem):
 
163
    """SchemaItem saved using repr() to save nested lists, dicts and tuples
 
164
    that store simple types.  The look is similar to JSON, but supports a
 
165
    couple different things, for example unicode and str values are distinct.
 
166
    The types that we support are bool, int, long, float, unicode, None and
 
167
    datetime.  Dictionary keys can also be byte strings (AKA str types)
 
168
    """
 
169
 
 
170
    VALID_TYPES = [bool, int, long, float, unicode, NoneType,
 
171
            datetime.datetime, time.struct_time]
 
172
 
 
173
    VALID_KEY_TYPES = VALID_TYPES + [str]
 
174
 
 
175
    def validate(self, data):
 
176
        if data is None:
 
177
            # let the super class handle the noneOkay attribute
 
178
            super(self, SchemaReprContainer).validate(data)
 
179
            return
 
180
        memory = set()
 
181
        to_validate = [data]
 
182
 
 
183
        while to_validate:
 
184
            obj = to_validate.pop()
 
185
            if id(obj) in memory:
 
186
                continue
 
187
            else:
 
188
                memory.add(id(obj))
 
189
 
 
190
            if isinstance(obj, list) or isinstance(obj, tuple):
 
191
                for item in obj:
 
192
                    to_validate.append(item)
 
193
            elif isinstance(obj, dict):
 
194
                for key, value in obj.items():
 
195
                    self.validateTypes(key, self.VALID_KEY_TYPES)
 
196
                    to_validate.append(value)
 
197
            else:
 
198
                self.validateTypes(obj, self.VALID_TYPES)
 
199
 
 
200
class SchemaList(SchemaReprContainer):
 
201
    """Special case of SchemaReprContainer that stores a simple list
 
202
 
 
203
    All values must have the same type
 
204
    """
 
205
    def __init__(self, childSchema, noneOk=False):
 
206
        super(SchemaList, self).__init__(noneOk)
 
207
        self.childSchema = childSchema
 
208
 
 
209
    def validate(self, data):
 
210
        if data is None:
 
211
            super(SchemaList, self).validate(data)
 
212
            return
 
213
        self.validateType(data, list)
 
214
        for i, value in enumerate(data):
 
215
            try:
 
216
                self.childSchema.validate(value)
 
217
            except ValidationError:
 
218
                raise ValidationError("%r (index: %s) has the wrong type" %
 
219
                        (value, i))
 
220
 
 
221
class SchemaDict(SchemaReprContainer):
 
222
    """Special case of SchemaReprContainer that stores a simple dict
 
223
 
 
224
    All keys and values must have the same type.
 
225
    """
 
226
 
 
227
    def __init__(self, keySchema, valueSchema, noneOk=False):
 
228
        super(SchemaDict, self).__init__(noneOk)
 
229
        self.keySchema = keySchema
 
230
        self.valueSchema = valueSchema
 
231
 
 
232
    def validate(self, data):
 
233
        if data is None:
 
234
            super(SchemaDict, self).validate(data)
 
235
            return
 
236
        self.validateType(data, dict)
 
237
        for key, value in data.items():
 
238
            try:
 
239
                self.keySchema.validate(key)
 
240
            except ValidationError:
 
241
                raise ValidationError("key %r has the wrong type" % key)
 
242
            try:
 
243
                self.valueSchema.validate(value)
 
244
            except ValidationError:
 
245
                raise ValidationError("value %r (key: %r) has the wrong type"
 
246
                        % (value, key))
 
247
 
 
248
class SchemaStatusContainer(SchemaReprContainer):
 
249
    """Version of SchemaReprContainer that stores the status dict for
 
250
    RemoteDownloaders.  It allows some values to be byte strings rather than
 
251
    unicode objects.
 
252
    """
 
253
 
 
254
 
 
255
    def validate(self, data):
 
256
        binary_fields = self._binary_fields()
 
257
        self.validateType(data, dict)
 
258
        for key, value in data.items():
 
259
            self.validateTypes(key, [bool, int, long, float, unicode,
 
260
                    str, NoneType, datetime.datetime, time.struct_time])
 
261
            if key not in binary_fields:
 
262
                self.validateTypes(value, [bool, int, long, float, unicode,
 
263
                        NoneType, datetime.datetime, time.struct_time])
 
264
            else:
 
265
                self.validateType(value, str)
 
266
 
 
267
    def _binary_fields(self):
 
268
        if FilenameType == unicode:
 
269
            return ('metainfo', 'fastResumeData')
 
270
        else:
 
271
            return ('channelName', 'shortFilename', 'filename', 'metainfo',
 
272
                    'fastResumeData')
 
273
 
 
274
class SchemaObject(SchemaItem):
 
275
    def __init__(self, klass, noneOk=False):
 
276
        super(SchemaObject, self).__init__(noneOk)
 
277
        self.klass = klass
 
278
 
 
279
    def validate(self, data):
 
280
        super(SchemaObject, self).validate(data)
 
281
        self.validateType(data, self.klass)
 
282
 
 
283
class ObjectSchema(object):
 
284
    """The schema to save/restore an object with.  Object schema isn't a
 
285
    SchemaItem, it's the schema for an entire object.
 
286
 
 
287
    Member variables:
 
288
 
 
289
    klass -- the python class
 
290
    classString -- a human readable string that represents objectClass
 
291
    fields -- list of  (name, SchemaItem) pairs.  One item for each attribute
 
292
        that should be stored to disk.
 
293
 
 
294
    Note: klass is always None in this module.  We don't need it for what we
 
295
    use the schema for, and we want to be able to delete old classes
 
296
    """
 
297
    pass
 
298
 
 
299
class DDBObjectSchema(ObjectSchema):
 
300
    klass = None
 
301
    classString = 'ddb-object'
 
302
    fields = [
 
303
        ('id', SchemaInt())
 
304
    ]
 
305
 
 
306
class IconCacheSchema (DDBObjectSchema):
 
307
    klass = None
 
308
    classString = 'icon-cache'
 
309
    fields = DDBObjectSchema.fields + [
 
310
        ('etag', SchemaString(noneOk=True)),
 
311
        ('modified', SchemaString(noneOk=True)),
 
312
        ('filename', SchemaFilename(noneOk=True)),
 
313
        ('url', SchemaURL(noneOk=True)),
 
314
        ]
 
315
 
 
316
class ItemSchema(DDBObjectSchema):
 
317
    klass = None
 
318
    classString = 'item'
 
319
    fields = DDBObjectSchema.fields + [
 
320
        ('feed_id', SchemaInt(noneOk=True)),
 
321
        ('downloader_id', SchemaInt(noneOk=True)),
 
322
        ('parent_id', SchemaInt(noneOk=True)),
 
323
        ('seen', SchemaBool()),
 
324
        ('autoDownloaded', SchemaBool()),
 
325
        ('pendingManualDL', SchemaBool()),
 
326
        ('pendingReason', SchemaString()),
 
327
        ('title', SchemaString()),
 
328
        ('expired', SchemaBool()),
 
329
        ('keep', SchemaBool()),
 
330
        ('creationTime', SchemaDateTime()),
 
331
        ('linkNumber', SchemaInt(noneOk=True)),
 
332
        ('icon_cache_id', SchemaInt(noneOk=True)),
 
333
        ('downloadedTime', SchemaDateTime(noneOk=True)),
 
334
        ('watchedTime', SchemaDateTime(noneOk=True)),
 
335
        ('isContainerItem', SchemaBool(noneOk=True)),
 
336
        ('videoFilename', SchemaFilename()),
 
337
        ('isVideo', SchemaBool()),
 
338
        ('releaseDateObj', SchemaDateTime()),
 
339
        ('eligibleForAutoDownload', SchemaBool()),
 
340
        ('duration', SchemaInt(noneOk=True)),
 
341
        ('screenshot', SchemaFilename(noneOk=True)),
 
342
        ('resumeTime', SchemaInt()),
 
343
        ('channelTitle', SchemaString(noneOk=True)),
 
344
        ('license', SchemaString(noneOk=True)),
 
345
        ('rss_id', SchemaString(noneOk=True)),
 
346
        ('thumbnail_url', SchemaURL(noneOk=True)),
 
347
        ('entry_title', SchemaString(noneOk=True)),
 
348
        ('raw_descrption', SchemaString(noneOk=False)),
 
349
        ('link', SchemaURL(noneOk=False)),
 
350
        ('payment_link', SchemaURL(noneOk=False)),
 
351
        ('comments_link', SchemaURL(noneOk=False)),
 
352
        ('url', SchemaURL(noneOk=False)),
 
353
        ('enclosure_size', SchemaInt(noneOk=True)),
 
354
        ('enclosure_type', SchemaString(noneOk=True)),
 
355
        ('enclosure_format', SchemaString(noneOk=True)),
 
356
        ('feedparser_output', SchemaReprContainer()),
 
357
    ]
 
358
 
 
359
class FileItemSchema(ItemSchema):
 
360
    klass = None
 
361
    classString = 'file-item'
 
362
    fields = ItemSchema.fields + [
 
363
        ('filename', SchemaFilename()),
 
364
        ('deleted', SchemaBool()),
 
365
        ('shortFilename', SchemaFilename(noneOk=True)),
 
366
        ('offsetPath', SchemaFilename(noneOk=True)),
 
367
    ]
 
368
 
 
369
class FeedSchema(DDBObjectSchema):
 
370
    klass = None
 
371
    classString = 'feed'
 
372
    fields = DDBObjectSchema.fields + [
 
373
        ('origURL', SchemaURL()),
 
374
        ('baseTitle', SchemaString(noneOk=True)),
 
375
        ('errorState', SchemaBool()),
 
376
        ('loading', SchemaBool()),
 
377
        ('feed_impl_id', SchemaInt()),
 
378
        ('icon_cache_id', SchemaInt(noneOk=True)),
 
379
        ('folder_id', SchemaInt(noneOk=True)),
 
380
        ('searchTerm', SchemaString(noneOk=True)),
 
381
        ('userTitle', SchemaString(noneOk=True)),
 
382
        ('autoDownloadable', SchemaBool()),
 
383
        ('getEverything', SchemaBool()),
 
384
        ('maxNew', SchemaInt()),
 
385
        ('maxOldItems', SchemaInt(noneOk=True)),
 
386
        ('fallBehind', SchemaInt()),
 
387
        ('expire', SchemaString()),
 
388
        ('expireTime', SchemaTimeDelta(noneOk=True)),
 
389
        ('section', SchemaString()),
 
390
        ('visible', SchemaBool()),
 
391
    ]
 
392
 
 
393
class FeedImplSchema(DDBObjectSchema):
 
394
    klass = None
 
395
    classString = 'field-impl'
 
396
    fields = DDBObjectSchema.fields + [
 
397
        ('url', SchemaURL()),
 
398
        ('ufeed_id', SchemaInt()),
 
399
        ('title', SchemaString(noneOk=True)),
 
400
        ('created', SchemaDateTime()),
 
401
        ('lastViewed', SchemaDateTime()),
 
402
        ('thumbURL', SchemaURL(noneOk=True)),
 
403
        ('updateFreq', SchemaInt()),
 
404
        ('initialUpdate', SchemaBool()),
 
405
    ]
 
406
 
 
407
class RSSFeedImplSchema(FeedImplSchema):
 
408
    klass = None
 
409
    classString = 'rss-feed-impl'
 
410
    fields = FeedImplSchema.fields + [
 
411
        ('initialHTML', SchemaBinary(noneOk=True)),
 
412
        ('etag', SchemaString(noneOk=True)),
 
413
        ('modified', SchemaString(noneOk=True)),
 
414
    ]
 
415
 
 
416
class RSSMultiFeedImplSchema(FeedImplSchema):
 
417
    klass = None
 
418
    classString = 'rss-multi-feed-impl'
 
419
    fields = FeedImplSchema.fields + [
 
420
        ('etag', SchemaDict(SchemaString(),SchemaString(noneOk=True))),
 
421
        ('modified', SchemaDict(SchemaString(),SchemaString(noneOk=True))),
 
422
        ('query', SchemaString(noneOk=True)),
 
423
    ]
 
424
 
 
425
class ScraperFeedImplSchema(FeedImplSchema):
 
426
    klass = None
 
427
    classString = 'scraper-feed-impl'
 
428
    fields = FeedImplSchema.fields + [
 
429
        ('initialHTML', SchemaBinary(noneOk=True)),
 
430
        ('initialCharset', SchemaString(noneOk=True)),
 
431
        ('linkHistory', SchemaReprContainer()),
 
432
    ]
 
433
 
 
434
class SearchFeedImplSchema(RSSMultiFeedImplSchema):
 
435
    klass = None
 
436
    classString = 'search-feed-impl'
 
437
    fields = RSSMultiFeedImplSchema.fields + [
 
438
        ('searching', SchemaBool()),
 
439
        ('lastEngine', SchemaString()),
 
440
        ('lastQuery', SchemaString()),
 
441
    ]
 
442
 
 
443
class DirectoryWatchFeedImplSchema(FeedImplSchema):
 
444
    klass = None
 
445
    classString = 'directory-watch-feed-impl'
 
446
    fields = FeedImplSchema.fields + [
 
447
        ('firstUpdate', SchemaBool()),
 
448
        ('dir', SchemaFilename(noneOk=True)),
 
449
        ]
 
450
 
 
451
class DirectoryFeedImplSchema(FeedImplSchema):
 
452
    klass = None
 
453
    classString = 'directory-feed-impl'
 
454
    # DirectoryFeedImpl doesn't have any addition fields over FeedImpl
 
455
 
 
456
class SearchDownloadsFeedImplSchema(FeedImplSchema):
 
457
    klass = None
 
458
    classString = 'search-downloads-feed-impl'
 
459
    # SearchDownloadsFeedImpl doesn't have any addition fields over FeedImpl
 
460
 
 
461
class ManualFeedImplSchema(FeedImplSchema):
 
462
    klass = None
 
463
    classString = 'manual-feed-impl'
 
464
    # no addition fields over FeedImplSchema
 
465
 
 
466
class SingleFeedImplSchema(FeedImplSchema):
 
467
    klass = None
 
468
    classString = 'single-feed-impl'
 
469
    # no addition fields over FeedImplSchema
 
470
 
 
471
class RemoteDownloaderSchema(DDBObjectSchema):
 
472
    klass = None
 
473
    classString = 'remote-downloader'
 
474
    fields = DDBObjectSchema.fields + [
 
475
        ('url', SchemaURL()),
 
476
        ('origURL', SchemaURL()),
 
477
        ('dlid', SchemaString()),
 
478
        ('contentType', SchemaString(noneOk=True)),
 
479
        ('channelName', SchemaFilename(noneOk=True)),
 
480
        ('status', SchemaStatusContainer()),
 
481
        ('manualUpload', SchemaBool()),
 
482
    ]
 
483
 
 
484
class HTTPAuthPasswordSchema(DDBObjectSchema):
 
485
    klass = None
 
486
    classString = 'http-auth-password'
 
487
    fields = DDBObjectSchema.fields + [
 
488
        ('username', SchemaString()),
 
489
        ('password', SchemaString()),
 
490
        ('host', SchemaString()),
 
491
        ('realm', SchemaString()),
 
492
        ('path', SchemaString()),
 
493
        ('authScheme', SchemaString()),
 
494
    ]
 
495
 
 
496
class ChannelFolderSchema(DDBObjectSchema):
 
497
    klass = None
 
498
    classString = 'channel-folder'
 
499
    fields = DDBObjectSchema.fields + [
 
500
        ('expanded', SchemaBool()),
 
501
        ('title', SchemaString()),
 
502
        ('section', SchemaString()),
 
503
    ]
 
504
 
 
505
class PlaylistFolderSchema(DDBObjectSchema):
 
506
    klass = None
 
507
    classString = 'playlist-folder'
 
508
    fields = DDBObjectSchema.fields + [
 
509
        ('expanded', SchemaBool()),
 
510
        ('title', SchemaString()),
 
511
        ('item_ids', SchemaList(SchemaInt())),
 
512
    ]
 
513
 
 
514
class PlaylistSchema(DDBObjectSchema):
 
515
    klass = None
 
516
    classString = 'playlist'
 
517
    fields = DDBObjectSchema.fields + [
 
518
        ('title', SchemaString()),
 
519
        ('item_ids', SchemaList(SchemaInt())),
 
520
        ('folder_id', SchemaInt(noneOk=True)),
 
521
    ]
 
522
 
 
523
class TabOrderSchema(DDBObjectSchema):
 
524
    klass = None
 
525
    classString = 'taborder-order'
 
526
    fields = DDBObjectSchema.fields + [
 
527
        ('type', SchemaString()),
 
528
        ('tab_ids', SchemaList(SchemaInt())),
 
529
    ]
 
530
 
 
531
class ChannelGuideSchema(DDBObjectSchema):
 
532
    klass = None
 
533
    classString = 'channel-guide'
 
534
    fields = DDBObjectSchema.fields + [
 
535
        ('url', SchemaURL(noneOk=True)),
 
536
        ('allowedURLs', SchemaList(SchemaURL())),
 
537
        ('updated_url', SchemaURL(noneOk=True)),
 
538
        ('favicon', SchemaURL(noneOk=True)),
 
539
        ('title', SchemaString(noneOk=True)),
 
540
        ('userTitle', SchemaString(noneOk=True)),
 
541
        ('icon_cache_id', SchemaInt(noneOk=True)),
 
542
        ('firstTime', SchemaBool()),
 
543
    ]
 
544
 
 
545
class ThemeHistorySchema(DDBObjectSchema):
 
546
    klass = None
 
547
    classString = 'theme-history'
 
548
    fields = DDBObjectSchema.fields + [
 
549
        ('lastTheme', SchemaString(noneOk=True)),
 
550
        ('pastThemes', SchemaList(SchemaString(noneOk=True), noneOk=False)),
 
551
    ]
 
552
 
 
553
class WidgetsFrontendStateSchema(DDBObjectSchema):
 
554
    klass = None
 
555
    classString = 'widgets-frontend-state'
 
556
    fields = DDBObjectSchema.fields + [
 
557
        ('list_view_displays', SchemaList(SchemaBinary())),
 
558
    ]
 
559
 
 
560
VERSION = 79
 
561
objectSchemas = [
 
562
    IconCacheSchema, ItemSchema, FileItemSchema, FeedSchema,
 
563
    FeedImplSchema, RSSFeedImplSchema, RSSMultiFeedImplSchema, ScraperFeedImplSchema,
 
564
    SearchFeedImplSchema, DirectoryFeedImplSchema, DirectoryWatchFeedImplSchema,
 
565
    SearchDownloadsFeedImplSchema, RemoteDownloaderSchema,
 
566
    HTTPAuthPasswordSchema, ChannelGuideSchema, ManualFeedImplSchema, SingleFeedImplSchema,
 
567
    PlaylistSchema, ChannelFolderSchema, PlaylistFolderSchema,
 
568
    TabOrderSchema, ThemeHistorySchema, WidgetsFrontendStateSchema
 
569
]