From d069c1b8af03b8c7cebe5e7a64a5f9ef52545362 Mon Sep 17 00:00:00 2001 From: maxkoryukov Date: Wed, 13 Jan 2016 04:48:03 +0500 Subject: [PATCH 1/6] * Fixed issue with non-working 'directories'-options under soft-chroot (fix maxkoryukov/CouchPotatoServer#8) * 'writable' was replaced with 'readonly' for UI options. This way is more native to CP configs * JS settins don't work with **delimiter** for "directories". It is done on the server now --- Gruntfile.js | 2 +- couchpotato/core/settings.py | 131 ++++++++++++++++-------- couchpotato/static/scripts/combined.base.min.js | 7 +- couchpotato/static/scripts/page/settings.js | 13 ++- couchpotato/static/style/combined.min.css | 82 ++++++++------- 5 files changed, 137 insertions(+), 98 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index a571c1d..8decace 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -70,7 +70,7 @@ module.exports = function(grunt){ options: { compass: true, update: true, - sourcemap: 'none' + //sourcemap: 'none' }, server: { files: [{ diff --git a/couchpotato/core/settings.py b/couchpotato/core/settings.py index 963e5c0..ff923a8 100644 --- a/couchpotato/core/settings.py +++ b/couchpotato/core/settings.py @@ -58,6 +58,8 @@ class Settings(object): self.file = None self.p = None self.log = None + self.soft_chroot = None + self.directories_delimiter = "::" def setFile(self, config_file): self.file = config_file @@ -106,6 +108,13 @@ class Settings(object): if save: self.save() + def getSoftChroot(self): + if not self.soft_chroot: + soft_chroot_dir = self.get('soft_chroot', default = None) + self.soft_chroot = SoftChroot(soft_chroot_dir) + + return self.soft_chroot + def set(self, section, option, value): if not self.isOptionWritable(section, option): self.log.warning('set::option "%s.%s" isn\'t writable', (section, option)) @@ -114,7 +123,6 @@ class Settings(object): self.log.warning('set::option "%s.%s" cancelled, since it is a META option', (section, option)) return None - return self.p.set(section, option, value) def get(self, option = '', section = 'core', default = None, type = None): @@ -123,9 +131,7 @@ class Settings(object): return None try: - - try: type = self.types[section][option] - except: type = 'unicode' if not type else type + type = self.getType(section, option) if hasattr(self, 'get%s' % type.capitalize()): return getattr(self, 'get%s' % type.capitalize())(section, option) @@ -168,6 +174,12 @@ class Settings(object): except: return tryFloat(self.p.get(section, option)) + def getDirectories(self, section, option): + value = self.p.get(section, option) + if value: + return map(str.strip, str.split(value, self.directories_delimiter)) + return [] + def getUnicode(self, section, option): value = self.p.get(section, option).decode('unicode_escape') return toUnicode(value).strip() @@ -198,13 +210,24 @@ class Settings(object): #if not self.isOptionReadable(section, option_name): # continue - is_password = False - try: is_password = self.types[section][option_name] == 'password' - except: pass + value = self.get(option_name, section) - values[section][option_name] = self.get(option_name, section) - if is_password and values[section][option_name]: - values[section][option_name] = len(values[section][option_name]) * '*' + is_password = self.getType(section, option_name) == 'password' + if is_password and value: + value = len(value) * '*' + + # chrotify directory before sending to UI: + if (self.getType(section, option_name) == 'directory') and value: + try: value = self.getSoftChroot().cut(str(value)) + except: value = "" + # chrotify directories before sending to UI: + if (self.getType(section, option_name) == 'directories'): + if (not value): + value = [] + try : value = map(lambda xx : self.getSoftChroot().cut(str(xx)), value) + except : value = [] + + values[section][option_name] = value return values @@ -228,6 +251,12 @@ class Settings(object): self.types[section][option] = type + def getType(self, section, option): + type = None + try: type = self.types[section][option] + except: type = 'unicode' if not type else type + return type + def addOptions(self, section_name, options): # no additional actions (related to ro-rw options) are required here if not self.options.get(section_name): @@ -243,36 +272,42 @@ class Settings(object): res = {} - if isinstance(self.options, dict): - for section_key in self.options.keys(): - section = self.options[section_key] - section_name = section.get('name') if 'name' in section else section_key - if self.isSectionReadable(section_name) and isinstance(section, dict): - s = {} - sg = [] - for section_field in section: - if section_field.lower() != 'groups': - s[section_field] = section[section_field] - else: - groups = section['groups'] - for group in groups: - g = {} - go = [] - for group_field in group: - if group_field.lower() != 'options': - g[group_field] = group[group_field] - else: - for option in group[group_field]: - option_name = option.get('name') - if self.isOptionReadable(section_name, option_name): - go.append(option) - option['writable'] = self.isOptionWritable(section_name, option_name) - if len(go)>0: - g['options'] = go - sg.append(g) - if len(sg)>0: - s['groups'] = sg - res[section_key] = s + # it is required to filter invisible options for UI, but also we should + # preserve original tree for server's purposes. + # So, next loops do one thing: copy options to res and in the process + # 1. omit NON-READABLE (for UI) options, and + # 2. put flags on READONLY options + for section_key in self.options.keys(): + section_orig = self.options[section_key] + section_name = section_orig.get('name') if 'name' in section_orig else section_key + if self.isSectionReadable(section_name): + section_copy = {} + section_copy_groups = [] + for section_field in section_orig: + if section_field.lower() != 'groups': + section_copy[section_field] = section_orig[section_field] + else: + for group_orig in section_orig['groups']: + group_copy = {} + group_copy_options = [] + for group_field in group_orig: + if group_field.lower() != 'options': + group_copy[group_field] = group_orig[group_field] + else: + for option in group_orig[group_field]: + option_name = option.get('name') + # You should keep in mind, that READONLY = !IS_WRITABLE + # and IS_READABLE is a different thing + if self.isOptionReadable(section_name, option_name): + group_copy_options.append(option) + if not self.isOptionWritable(section_name, option_name): + option['readonly'] = True + if len(group_copy_options)>0: + group_copy['options'] = group_copy_options + section_copy_groups.append(group_copy) + if len(section_copy_groups)>0: + section_copy['groups'] = section_copy_groups + res[section_key] = section_copy return res @@ -288,10 +323,18 @@ class Settings(object): option = kwargs.get('name') value = kwargs.get('value') - if (section in self.types) and (option in self.types[section]) and (self.types[section][option] == 'directory'): - soft_chroot_dir = self.get('soft_chroot', default = None) - soft_chroot = SoftChroot(soft_chroot_dir) - value = soft_chroot.add(str(value)) + self.log.debug("value-> %s", (value)); + + if self.getType(section, option) == 'directory': + value = self.getSoftChroot().add(str(value)) + + if self.getType(section, option) == 'directories': + import json + value = json.loads(value) + if not (value and isinstance(value, list)): + value = [] + value = map(lambda xx : self.getSoftChroot().add(str(xx)), value) + value = self.directories_delimiter.join(value) # See if a value handler is attached, use that as value new_value = fireEvent('setting.save.%s.%s' % (section, option), value, single = True) diff --git a/couchpotato/static/scripts/combined.base.min.js b/couchpotato/static/scripts/combined.base.min.js index a37a2a1..ccc58cf 100644 --- a/couchpotato/static/scripts/combined.base.min.js +++ b/couchpotato/static/scripts/combined.base.min.js @@ -1342,7 +1342,7 @@ var OptionBase = new Class({ self.section = section; self.name = name; self.value = self.previous_value = value; - self.read_only = !(options && options.writable); + self.read_only = !(options && !options.readonly); self.createBase(); self.create(); self.createHint(); @@ -1784,12 +1784,11 @@ Option.Directory = new Class({ Option.Directories = new Class({ Extends: Option.String, directories: [], - delimiter: "::", afterInject: function() { var self = this; self.el.setStyle("display", "none"); self.directories = []; - self.getValue().split(self.delimiter).each(function(value) { + self.getSettingValue().each(function(value) { self.addDirectory(value); }); self.addDirectory(); @@ -1833,7 +1832,7 @@ Option.Directories = new Class({ dirs.include(dir.getValue()); } else $(dir).addClass("is_empty"); }); - self.input.set("value", dirs.join(self.delimiter)); + self.input.set("value", JSON.encode(dirs)); self.input.fireEvent("change"); self.addDirectory(); } diff --git a/couchpotato/static/scripts/page/settings.js b/couchpotato/static/scripts/page/settings.js index 0136cc8..042a780 100644 --- a/couchpotato/static/scripts/page/settings.js +++ b/couchpotato/static/scripts/page/settings.js @@ -343,7 +343,7 @@ var OptionBase = new Class({ self.section = section; self.name = name; self.value = self.previous_value = value; - self.read_only = !(options && options.writable); + self.read_only = !(options && !options.readonly); self.createBase(); self.create(); @@ -991,7 +991,6 @@ Option.Directories = new Class({ Extends: Option.String, directories: [], - delimiter: '::', afterInject: function(){ var self = this; @@ -999,9 +998,11 @@ Option.Directories = new Class({ self.el.setStyle('display', 'none'); self.directories = []; - self.getValue().split(self.delimiter).each(function(value){ + + self.getSettingValue().each(function(value){ self.addDirectory(value); }); + self.addDirectory(); }, @@ -1025,7 +1026,7 @@ Option.Directories = new Class({ else $(dir).inject(dirs.getLast(), 'after'); - // Replace some properties + // TODO : Replace some properties dir.save = self.saveItems.bind(self); $(dir).getElement('label').set('text', 'Movie Folder'); $(dir).getElement('.formHint').destroy(); @@ -1068,14 +1069,12 @@ Option.Directories = new Class({ $(dir).addClass('is_empty'); }); - self.input.set('value', dirs.join(self.delimiter)); + self.input.set('value', JSON.encode(dirs) ); self.input.fireEvent('change'); self.addDirectory(); } - - }); Option.Choice = new Class({ diff --git a/couchpotato/static/style/combined.min.css b/couchpotato/static/style/combined.min.css index 0380ab7..2977344 100644 --- a/couchpotato/static/style/combined.min.css +++ b/couchpotato/static/style/combined.min.css @@ -1,6 +1,6 @@ .page.movie_details,.thumb_list .movie.hover_start .actions{pointer-events:none} .movies>.description a:hover,.page.movie_details .releases .buttons a:hover{text-decoration:underline} -.update.message{background:#FFF;padding:20px;text-align:center;font-size:1.25em} +.update.message{background:#fff;padding:20px;text-align:center;font-size:1.25em} .dark .update.message{background:#2d2d2d} @media (max-width:768px){.update.message{font-size:1em} } @@ -14,7 +14,7 @@ .dark .search_form .wrapper{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} .search_form .wrapper:before{-webkit-transform:rotate(45deg);transform:rotate(45deg);content:'';display:block;position:absolute;height:10px;width:10px;background:#ac0000;left:-6px;bottom:16px;z-index:1} .dark .search_form .wrapper:before{background:#f85c22} -.search_form .input{background:#FFF;border-radius:3px 0 0 3px;position:relative;left:4px;height:44px;overflow:hidden;width:100%} +.search_form .input{background:#fff;border-radius:3px 0 0 3px;position:relative;left:4px;height:44px;overflow:hidden;width:100%} .dark .search_form .input{background:#2d2d2d} .search_form .input input{position:absolute;top:0;left:0;height:100%;width:100%;z-index:1} .search_form .input input::-ms-clear{width:0;height:0} @@ -29,7 +29,7 @@ .page.home .search_form,.search_form.focused,.search_form.shown{border-color:#04bce6} .page.home .search_form .wrapper,.search_form.focused .wrapper,.search_form.shown .wrapper{display:block;width:380px;-webkit-transform-origin:0 90%;transform-origin:0 90%} .page.home .search_form .input input,.search_form.focused .input input,.search_form.shown .input input{opacity:1} -.search_form .results_container{min-height:50px;text-align:left;position:relative;left:4px;display:none;background:#FFF;border-radius:3px 0 0;overflow:hidden} +.search_form .results_container{min-height:50px;text-align:left;position:relative;left:4px;display:none;background:#fff;border-radius:3px 0 0;overflow:hidden} .dark .search_form .results_container{background:#2d2d2d} .search_form .results_container .results{max-height:280px;overflow-x:hidden} .search_form .results_container .results .media_result{overflow:hidden;height:50px;position:relative} @@ -56,7 +56,7 @@ .dark .search_form .results_container .results .media_result .options .button{background:#f85c22} .search_form .results_container .results .media_result .options .message{font-size:20px;color:#fff} .search_form .results_container .results .media_result .thumbnail{width:30px;min-height:100%;display:block;margin:0;vertical-align:top} -.search_form .results_container .results .media_result .data{position:absolute;height:100%;top:0;left:30px;right:0;cursor:pointer;border-top:1px solid rgba(255,255,255,.08);transition:all .4s cubic-bezier(.9,0,.1,1);will-change:transform;-webkit-transform:translateX(0) rotateZ(360deg);transform:translateX(0) rotateZ(360deg);background:#FFF} +.search_form .results_container .results .media_result .data{position:absolute;height:100%;top:0;left:30px;right:0;cursor:pointer;border-top:1px solid rgba(255,255,255,.08);transition:all .4s cubic-bezier(.9,0,.1,1);will-change:transform;-webkit-transform:translateX(0) rotateZ(360deg);transform:translateX(0) rotateZ(360deg);background:#fff} .dark .search_form .results_container .results .media_result .data{background:#2d2d2d;border-color:rgba(255,255,255,.08)} @media (max-width:480px){.search_form .results_container .results .media_result .thumbnail{display:none} .search_form .results_container .results .media_result .data{left:0} @@ -79,7 +79,7 @@ } .page.home .search_form .icon-search{display:block;color:#000;right:20px;top:20px;width:66px;height:66px;line-height:66px;left:auto;-webkit-transform:none;transform:none;font-size:2em;opacity:.5} .page.home .search_form .wrapper:before,.page.movies .scroll_content{display:none} -.dark .page.home .search_form .icon-search{color:#FFF} +.dark .page.home .search_form .icon-search{color:#fff} .page.home .search_form .icon-search:hover{background:0 0} @media (max-width:480px){.page.home .search_form .icon-search{width:44px;height:44px;line-height:44px;right:10px;top:10px;font-size:1.5em} } @@ -87,7 +87,7 @@ @media (max-width:480px){.page.home .search_form .wrapper{right:10px;top:10px;left:10px} } .page.home .search_form .wrapper .input{border-radius:0;left:0;position:absolute;top:0;height:66px} -.page.home .search_form .wrapper .input input{box-shadow:0;font-size:2em;font-weight:400;padding-right:66px;background:#FFF} +.page.home .search_form .wrapper .input input{box-shadow:0;font-size:2em;font-weight:400;padding-right:66px;background:#fff} .dark .page.home .search_form .wrapper .input input{background:#2d2d2d} .page.home .search_form .wrapper .results_container{min-height:66px;position:absolute;top:66px;left:0;right:0;border:1px solid #ebebeb;border-top:0} .dark .page.home .search_form .wrapper .results_container{border-color:#353535} @@ -95,17 +95,15 @@ .page.home .search_form .wrapper .input input{padding-right:44px;font-size:1em} .page.home .search_form .wrapper .results_container{top:44px;min-height:44px} } -@media (min-width:480px){.page.home .search_form .wrapper .results_container .results{max-height:400px} +@media (min-width:480px){.page.home .search_form .wrapper .results_container .results .media_result .data,.page.home .search_form .wrapper .results_container .results .media_result .options{left:40px} +.page.home .search_form .wrapper .results_container .results{max-height:400px} .page.home .search_form .wrapper .results_container .results .media_result{height:66px} .page.home .search_form .wrapper .results_container .results .media_result .thumbnail{width:40px} -.page.home .search_form .wrapper .results_container .results .media_result .options{left:40px} .page.home .search_form .wrapper .results_container .results .media_result .options .title{margin-right:5px;width:320px} } @media (min-width:480px) and (max-width:480px){.page.home .search_form .wrapper .results_container .results .media_result{height:44px} .page.home .search_form .wrapper .results_container .results .media_result .options .title{width:140px;margin-right:2px} } -@media (min-width:480px){.page.home .search_form .wrapper .results_container .results .media_result .data{left:40px} -} @media (max-width:480px){.page.home .search_form .wrapper .results_container .results .media_result{height:44px} .page.home .search_form .wrapper .results_container .results .media_result .options .title{width:140px;margin-right:2px} } @@ -131,9 +129,9 @@ .movie .quality span{display:inline-block;background:#ebebeb;border:1px solid transparent;color:rgba(0,0,0,.5);border-radius:1px;padding:1px 3px} .dark .movie .quality span{color:rgba(255,255,255,.5)} .movie .quality span.failed{background:#993619;color:#FFF} -.movie .quality span.available{color:#009902;border-color:#009902;background:#FFF} -.movie .quality span.snatched{background:#548399;color:#FFF} -.movie .quality span.done,.movie .quality span.downloaded{background:#009902;color:#FFF} +.movie .quality span.available{color:#009902;border-color:#009902;background:#fff} +.movie .quality span.snatched{background:#548399;color:#fff} +.movie .quality span.done,.movie .quality span.downloaded{background:#009902;color:#fff} .dark .movie .quality span{background:0 0} .dark .movie .quality span.available{border-color:transparent;background:0 0} .dark .movie .quality span.snatched{background:#548399} @@ -157,11 +155,11 @@ .movies>.description{position:absolute;top:0;right:20px;width:auto;line-height:80px;opacity:.7} .movies>.description a{color:#ac0000;display:inline} .dark .movies>.description a{color:#f85c22} -.movies>.loading{background:#FFF} +.movies>.loading{background:#fff} .dark .movies>.loading{background:#2d2d2d} .movies>.loading .message{color:#000} -.dark .movies>.loading .message{color:#FFF} -.movies>.loading .spinner{background-color:#FFF} +.dark .movies>.loading .message{color:#fff} +.movies>.loading .spinner{background-color:#fff} .dark .movies>.loading .spinner{background-color:#2d2d2d} .movies .movie .actions{will-change:transform,opacity;-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)} .movies .progress div{width:50%;padding:5px 10px;display:-webkit-flex;display:-ms-flexbox;display:flex} @@ -192,11 +190,11 @@ .list_list .movie .info .rating .vote{display:inline-block;min-width:60px;text-align:right} .list_list .movie .actions{position:absolute;right:10px;top:0;bottom:0;display:none;z-index:10} .list_list .movie .actions .action{display:inline-block} -.list_list .movie .actions a{height:100%;display:block;background:#FFF;color:#ac0000;padding:10px;width:auto;float:right;text:#000} +.list_list .movie .actions a{height:100%;display:block;background:#fff;color:#ac0000;padding:10px;width:auto;float:right;text:#000} .list_list .movie .actions a .icon,.list_list .movie .actions a:before{display:none} -.dark .list_list .movie .actions a{background:#2d2d2d;color:#f85c22;text:#FFF} +.dark .list_list .movie .actions a{background:#2d2d2d;color:#f85c22;text:#fff} .list_list .movie .actions a:hover{background:#ebebeb;color:#000} -.dark .list_list .movie .actions a:hover{background:#353535;color:#FFF} +.dark .list_list .movie .actions a:hover{background:#353535;color:#fff} .list_list .movie:hover .actions{display:block} @media (max-width:768px){.list_list .movie .info .quality span{margin:2px 2px 0 0} .list_list .movie:hover .actions{display:none} @@ -268,7 +266,7 @@ .thumb_list .movie .actions .action{position:relative;margin-right:10px;float:right;clear:both} .thumb_list .movie .actions .action:first-child{margin-top:10px} .thumb_list .movie .actions .action a{transition:all 150ms cubic-bezier(.9,0,.1,1);will-change:color,background;transition-property:color,background;display:block;width:auto;padding:6.67px;color:#FFF;border-radius:2px;font-weight:400} -.thumb_list .movie .actions .action a:hover{background:#FFF;color:#ac0000} +.thumb_list .movie .actions .action a:hover{background:#fff;color:#ac0000} .dark .thumb_list .movie .actions .action a:hover{background:#2d2d2d;color:#FFF} .thumb_list .movie:hover .actions{opacity:1;visibility:visible} .thumb_list .movie .mask{bottom:44px;border-radius:3px;will-change:opacity;transition:opacity 30ms} @@ -281,7 +279,7 @@ } .page.movie_details .overlay .close{display:inline-block;text-align:center;font-size:60px;line-height:80px;color:#FFF;width:100%;height:100%;opacity:0;will-change:opacity;transition:opacity .3s ease .2s} .page.movie_details .overlay .close:before{display:block;width:44px} -.page.movie_details .scroll_content{position:fixed;z-index:2;top:0;bottom:0;right:0;left:176px;background:#FFF;border-radius:3px 0 0 3px;overflow-y:auto;will-change:transform;-webkit-transform:translateX(100%) rotateZ(360deg);transform:translateX(100%) rotateZ(360deg);transition:-webkit-transform 450ms cubic-bezier(.9,0,.1,1);transition:transform 450ms cubic-bezier(.9,0,.1,1)} +.page.movie_details .scroll_content{position:fixed;z-index:2;top:0;bottom:0;right:0;left:176px;background:#fff;border-radius:3px 0 0 3px;overflow-y:auto;will-change:transform;-webkit-transform:translateX(100%) rotateZ(360deg);transform:translateX(100%) rotateZ(360deg);transition:-webkit-transform 450ms cubic-bezier(.9,0,.1,1);transition:transform 450ms cubic-bezier(.9,0,.1,1)} .dark .page.movie_details .scroll_content{background:#2d2d2d} .page.movie_details .scroll_content>.head{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;padding:0 20px 0 10px;position:relative;z-index:2;will-change:transform,opacity;-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)} .page.movie_details .scroll_content>.head h1{-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;margin:0;font-size:24px;font-weight:300;max-width:100%} @@ -293,13 +291,13 @@ .page.movie_details .scroll_content>.head h1 .more_menu .icon-dropdown:before{right:10px} } .page.movie_details .scroll_content>.head h1 .more_menu a{color:#000} -.dark .page.movie_details .scroll_content>.head h1 .more_menu a{color:#FFF} +.dark .page.movie_details .scroll_content>.head h1 .more_menu a{color:#fff} .page.movie_details .scroll_content>.head h1 .more_menu .icon-dropdown{padding-right:30px} .page.movie_details .scroll_content>.head .more_menu{display:inline-block;vertical-align:top;max-width:100%;margin-bottom:0} .page.movie_details .scroll_content>.head .more_menu>a{float:left;line-height:80px;color:#ac0000} .dark .page.movie_details .scroll_content>.head .more_menu>a{color:#f85c22} .page.movie_details .scroll_content>.head .more_menu>a:hover{color:#000} -.dark .page.movie_details .scroll_content>.head .more_menu>a:hover{color:#FFF} +.dark .page.movie_details .scroll_content>.head .more_menu>a:hover{color:#fff} .page.movie_details .scroll_content>.head .more_menu .icon-dropdown{position:relative;padding:0 25px 0 10px} .page.movie_details .scroll_content>.head .more_menu .icon-dropdown:before{position:absolute;right:10px;top:-2px;opacity:.2} .page.movie_details .scroll_content>.head .more_menu .icon-dropdown:hover:before{opacity:1} @@ -320,7 +318,7 @@ .page.movie_details .scroll_content>.head .buttons>a{display:inline-block;padding:0 10px;color:#ac0000;line-height:80px} .dark .page.movie_details .scroll_content>.head .buttons>a{color:#f85c22} .page.movie_details .scroll_content>.head .buttons>a:hover{background:#ebebeb;color:#000} -.dark .page.movie_details .scroll_content>.head .buttons>a:hover{background:#353535;color:#FFF} +.dark .page.movie_details .scroll_content>.head .buttons>a:hover{background:#353535;color:#fff} .page.movie_details .scroll_content .section{padding:20px;border-top:1px solid rgba(0,0,0,.1);will-change:transform,opacity;-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)} .dark .page.movie_details .scroll_content .section{border-color:rgba(255,255,255,.1)} @media (max-width:480px){.page.movie_details .scroll_content>.head .more_menu.title .wrapper{top:30px;max-width:240px} @@ -343,7 +341,7 @@ .page.movie_details .section_add .options>div .title{min-width:75px;width:2000px;margin:0 10px 0 0} .page.movie_details .section_add .options>div .category,.page.movie_details .section_add .options>div .profile{width:200px;min-width:50px;margin:0 10px 0 0} .page.movie_details .section_add .options>div .add{width:200px} -.page.movie_details .section_add .options>div .add .button{background:#FFF;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;display:block;text-align:center;width:100%;margin:0} +.page.movie_details .section_add .options>div .add .button{background:#fff;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;display:block;text-align:center;width:100%;margin:0} .dark .page.movie_details .section_add .options>div .add .button{background:#2d2d2d} .page.movie_details .section_add .options>div .add .button:hover{background:#ac0000} .dark .page.movie_details .section_add .options>div .add .button:hover{background:#f85c22} @@ -357,7 +355,7 @@ @media (max-width:480px){.page.movie_details .releases .item{display:block} } .page.movie_details .releases .item:not(.head):hover{background:#ebebeb;text:#000} -.dark .page.movie_details .releases .item:not(.head):hover{background:#353535;text:#FFF} +.dark .page.movie_details .releases .item:not(.head):hover{background:#353535;text:#fff} .page.movie_details .releases .item span{text-align:center} .page.movie_details .releases .item span:before{display:none;font-weight:700;opacity:.8;margin-right:3px;width:100%;font-size:.9em} .page.movie_details .releases .item .name{-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;text-align:left} @@ -372,7 +370,7 @@ .page.movie_details .releases .item .actions a{text-align:center} } .page.movie_details .releases .item .actions a{display:inline-block;vertical-align:top;padding:6.67px;min-width:26px;color:#000} -.dark .page.movie_details .releases .item .actions a{color:#FFF} +.dark .page.movie_details .releases .item .actions a{color:#fff} .page.movie_details .releases .item .actions a:hover{color:#ac0000} .dark .page.movie_details .releases .item .actions a:hover{color:#f85c22} .page.movie_details .releases .item .actions a:after{margin-left:3px;font-size:.9em} @@ -421,7 +419,7 @@ .page.movie_details .section_trailer.section_trailer .trailer_container:hover .icon-play,.page.movie_details .section_trailer.section_trailer .trailer_container:hover .icon-play span{opacity:1} .page.movie_details .section_trailer.section_trailer .trailer_container iframe{position:absolute;width:100%;height:100%;border:0;top:0;left:0;max-height:450px;z-index:10} .alph_nav{position:relative} -.alph_nav .mass_edit_form{display:-webkit-flex;display:-ms-flexbox;display:flex;background:#FFF;position:fixed;top:80px;right:0;left:132px;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center;will-change:max-height;transition:max-height .3s cubic-bezier(.9,0,.1,1);max-height:0;overflow:hidden} +.alph_nav .mass_edit_form{display:-webkit-flex;display:-ms-flexbox;display:flex;background:#fff;position:fixed;top:80px;right:0;left:132px;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center;will-change:max-height;transition:max-height .3s cubic-bezier(.9,0,.1,1);max-height:0;overflow:hidden} .dark .alph_nav .mass_edit_form{background:#2d2d2d} .mass_editing .alph_nav .mass_edit_form{max-height:44px} .alph_nav .mass_edit_form>*{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center} @@ -445,7 +443,7 @@ .alph_nav .menus .filter .button{margin-top:-2px} .alph_nav .menus .filter .search{position:relative} .alph_nav .menus .filter .search:before{position:absolute;height:100%;line-height:38px;padding-left:10px;font-size:16px;opacity:.5} -.alph_nav .menus .filter .search input{width:100%;padding:10px 10px 10px 30px;background:#FFF;border:#ebebeb;border-bottom:1px solid transparent} +.alph_nav .menus .filter .search input{width:100%;padding:10px 10px 10px 30px;background:#fff;border:#ebebeb;border-bottom:1px solid transparent} .dark .alph_nav .menus .filter .search input{background:#2d2d2d;border-color:#353535} .alph_nav .menus .filter .numbers{padding:10px} .alph_nav .menus .filter .numbers li{float:left;width:10%;height:30px;line-height:30px;text-align:center;opacity:.2;cursor:default;border:0} @@ -613,7 +611,7 @@ .api_docs .database table .form,.api_docs .database table form{width:600px} .api_docs .database table textarea{font-size:12px;width:100%;height:200px} .api_docs .database table input[type=submit]{display:block} -.page.login{background:#FFF;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;font-size:1.25em} +.page.login{background:#fff;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;font-size:1.25em} .dark .page.login{background:#2d2d2d} .page.login h1{padding:0 0 10px;font-size:60px;font-family:Lobster;font-weight:400;color:#ac0000;text-align:center} .dark .page.login h1{color:#f85c22} @@ -663,11 +661,11 @@ @font-face{font-family:Lobster;src:url(../fonts/Lobster-webfont.eot);src:url(../fonts/Lobster-webfont.eot?#iefix) format("embedded-opentype"),url(../fonts/Lobster-webfont.woff2) format("woff2"),url(../fonts/Lobster-webfont.woff) format("woff"),url(../fonts/Lobster-webfont.ttf) format("truetype"),url(../fonts/Lobster-webfont.svg#lobster_14regular) format("svg");font-weight:400;font-style:normal} *{margin:0;padding:0;box-sizing:border-box;text-rendering:optimizeSpeed;-webkit-backface-visibility:hidden;backface-visibility:hidden} body,html{font-size:14px;line-height:1.5;font-family:OpenSans,"Helvetica Neue",Helvetica,Arial,Geneva,sans-serif;font-weight:300;height:100%;margin:0;padding:0;background:#111;color:#000;overflow:hidden} -.dark body,.dark html{background:#111;color:#FFF} +.dark body,.dark html{background:#111;color:#fff} a{position:relative;overflow:hidden;text-decoration:none;cursor:pointer;-webkit-tap-highlight-color:transparent} a:active,a:visited{color:inherit} input,select,textarea{font-size:1em;font-weight:300;padding:6.67px;border-radius:0;border:1px solid #b8b8b8;background:#ebebeb;color:#000} -.dark input,.dark select,.dark textarea{border-color:#1b1b1b;background:#353535;color:#FFF} +.dark input,.dark select,.dark textarea{border-color:#1b1b1b;background:#353535;color:#fff} input[type=text],textarea{-webkit-appearance:none} .button{color:#ac0000;font-weight:300;padding:5px;cursor:pointer;border:1px solid #ac0000;border-radius:3px;margin:0 5px;transition:all 150ms} .dark .button{color:#f85c22;border-color:#f85c22} @@ -734,7 +732,7 @@ input[type=text],textarea{-webkit-appearance:none} .dark .corner_background{background:#f85c22} @media (max-width:480px){.corner_background{left:44px} } -.content{position:absolute;height:100%;top:0;left:132px;right:0;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:column nowrap;-ms-flex-flow:column nowrap;flex-flow:column nowrap;background:#FFF;border-radius:3px 0 0 3px} +.content{position:absolute;height:100%;top:0;left:132px;right:0;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:column nowrap;-ms-flex-flow:column nowrap;flex-flow:column nowrap;background:#fff;border-radius:3px 0 0 3px} @media (max-width:480px){.content{left:44px} } .dark .content{background:#2d2d2d} @@ -747,7 +745,7 @@ input[type=text],textarea{-webkit-appearance:none} .page.home .scroll_content{padding:0 0 20px} .page h1,.page h2,.page h3,.page h4{font-weight:300} .page h2{font-size:24px;padding:20px} -.page .navigation{z-index:2;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:fixed;top:0;height:80px;left:152px;right:20px;background:#FFF;border-radius:3px 0 0} +.page .navigation{z-index:2;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:fixed;top:0;height:80px;left:152px;right:20px;background:#fff;border-radius:3px 0 0} .more_menu .button,.more_menu a,.page .navigation ul li{display:inline-block} .more_menu,.more_menu .button:before{position:relative} .dark .page .navigation{background:#2d2d2d} @@ -757,7 +755,7 @@ input[type=text],textarea{-webkit-appearance:none} .page .navigation ul{-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;list-style:none} .page .navigation ul li a{display:inline-block;font-size:24px;line-height:80px;padding:0 20px;color:#000;opacity:.5;vertical-align:bottom} .page .navigation ul .active a,.page .navigation ul li a:hover,.question.show{opacity:1} -.dark .page .navigation ul li a{color:#FFF} +.dark .page .navigation ul li a{color:#fff} .page .navigation>ul>li:first-child{margin-left:-20px} @media (max-width:480px){.page .navigation ul li a{font-size:18px;line-height:44px;padding:0 10px} .page .navigation>ul>li:first-child{margin-left:-10px} @@ -776,13 +774,13 @@ input[type=text],textarea{-webkit-appearance:none} .dark .more_menu .wrapper{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} .more_menu .wrapper:before{-webkit-transform:rotate(45deg) translateY(-60%);transform:rotate(45deg) translateY(-60%);content:'';display:block;position:absolute;background:#ac0000;height:10px;width:10px;left:-9px;bottom:11px;z-index:1;opacity:1;border-radius:3px} .dark .more_menu .wrapper:before{background:#f85c22} -.more_menu .wrapper ul{background:#FFF;position:relative;z-index:2;overflow:hidden;border-radius:3px 0 0 3px} +.more_menu .wrapper ul{background:#fff;position:relative;z-index:2;overflow:hidden;border-radius:3px 0 0 3px} .dark .more_menu .wrapper ul{background:#2d2d2d} .more_menu .wrapper ul li{display:block;line-height:1em;border-top:1px solid transparent;border-color:#ebebeb} .dark .more_menu .wrapper ul li{border-color:#353535} .more_menu .wrapper ul li:first-child{border-top:0} .more_menu .wrapper ul li a{display:block;color:#000;padding:5px 10px;font-size:1em;line-height:22px;width:100%} -.dark .more_menu .wrapper ul li a{color:#FFF} +.dark .more_menu .wrapper ul li a{color:#fff} .more_menu .wrapper ul li a:hover{background:#ebebeb} .dark .more_menu .wrapper ul li a:hover{background:#353535} .more_menu .wrapper ul li:first-child a{padding-top:10px} @@ -791,7 +789,7 @@ input[type=text],textarea{-webkit-appearance:none} .messages .message{overflow:hidden;transition:all .6s cubic-bezier(.9,0,.1,1);width:100%;position:relative;max-height:0;font-size:1.1em;font-weight:400;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:100% 50%;transform-origin:100% 50%;background:#ac0000;margin-bottom:4px;border-radius:3px;box-shadow:0 0 15px 2px rgba(0,0,0,.15)} .mask,.messages .close{position:absolute;top:0;right:0} .dark .messages .message{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} -.messages .message .inner{padding:15px 30px 15px 20px;background:#FFF;margin-bottom:4px;border-radius:3px} +.messages .message .inner{padding:15px 30px 15px 20px;background:#fff;margin-bottom:4px;border-radius:3px} .dark .messages .message .inner{background:#2d2d2d} .messages .message.sticky .inner{background-color:#ac0000} .dark .messages .message.sticky .inner{background-color:#f85c22} @@ -849,7 +847,7 @@ input[type=text],textarea{-webkit-appearance:none} .page.settings .tabs li a{color:rgba(0,0,0,.5)} .dark .page.settings .tabs li a{color:rgba(255,255,255,.5)} .page.settings .tabs li.active a{color:#000} -.dark .page.settings .tabs li.active a{color:#FFF} +.dark .page.settings .tabs li.active a{color:#fff} .page.settings form.containers{margin:0 20px 0 0;-webkit-flex:1;-ms-flex:1;flex:1} @media (max-width:480px){.page.settings form.containers{margin:0 10px 0 0} } @@ -914,14 +912,14 @@ input[type=text],textarea{-webkit-appearance:none} .page.settings .switch{display:inline-block;background:#ac0000;height:20px;width:50px;min-width:0!important;transition:all 250ms;cursor:pointer;border-radius:20px} .dark .page.settings .switch{background:#f85c22} .page.settings .switch input{display:none} -.page.settings .switch .toggle{background:#FFF;margin:1px;height:18px;width:18px;transition:-webkit-transform 250ms;transition:transform 250ms;-webkit-transform:translateX(30px);transform:translateX(30px);border-radius:20px} +.page.settings .switch .toggle{background:#fff;margin:1px;height:18px;width:18px;transition:-webkit-transform 250ms;transition:transform 250ms;-webkit-transform:translateX(30px);transform:translateX(30px);border-radius:20px} .dark .page.settings .switch .toggle{background:#2d2d2d} .page.settings fieldset.enabler.disabled .switch,.page.settings:not(.show_advanced) .advanced_toggle .switch{background:#ebebeb;border-color:#ebebeb} .dark .page.settings fieldset.enabler.disabled .switch,.dark .page.settings:not(.show_advanced) .advanced_toggle .switch{background:#4e4e4e;border-color:#4e4e4e} .page.settings fieldset.enabler.disabled .switch .toggle,.page.settings:not(.show_advanced) .advanced_toggle .switch .toggle{-webkit-transform:translateX(0);transform:translateX(0)} .page.settings fieldset.enabler.disabled .switch:hover,.page.settings:not(.show_advanced) .advanced_toggle .switch:hover{background:#b8b8b8;border-color:#b8b8b8} .dark .page.settings fieldset.enabler.disabled .switch:hover,.dark .page.settings:not(.show_advanced) .advanced_toggle .switch:hover{background:#020202;border-color:#020202} -.page.settings .option_list{background:#FFF;margin-top:10px} +.page.settings .option_list{background:#fff;margin-top:10px} .dark .page.settings .option_list{background:#2d2d2d} .page.settings .option_list fieldset{position:relative} .page.settings .option_list fieldset h2 .group_label{min-width:100px} @@ -988,7 +986,7 @@ input[type=text],textarea{-webkit-appearance:none} .dark .directory_list{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} .directory_list .pointer{border-right:6px solid transparent;border-left:6px solid transparent;border-bottom:6px solid transparent;border-bottom-color:#ac0000;display:block;position:absolute;width:0;margin:-6px 0 0 100px} .dark .directory_list .pointer{border-bottom-color:#f85c22} -.directory_list .wrapper{background:#FFF;border-radius:3px 3px 0 0;margin-top:5px} +.directory_list .wrapper{background:#fff;border-radius:3px 3px 0 0;margin-top:5px} .dark .directory_list .wrapper{background:#2d2d2d} .directory_list ul{width:92%;height:300px;overflow:auto;margin:0 20px} .directory_list li{padding:4px 20px 4px 0;cursor:pointer;margin:0!important;border-top:1px solid rgba(255,255,255,.1);overflow:hidden;white-space:nowrap;text-overflow:ellipsis} From f763bcc161e611b7d5655e4bfd16c76331b0d929 Mon Sep 17 00:00:00 2001 From: maxkoryukov Date: Thu, 14 Jan 2016 07:55:12 +0500 Subject: [PATCH 2/6] small fixes. squash me --- couchpotato/core/plugins/test_browser.py | 7 ++++--- couchpotato/core/softchroot.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/couchpotato/core/plugins/test_browser.py b/couchpotato/core/plugins/test_browser.py index 1983d84..862a307 100644 --- a/couchpotato/core/plugins/test_browser.py +++ b/couchpotato/core/plugins/test_browser.py @@ -1,13 +1,14 @@ import sys import os import logging -import unittest -from unittest import TestCase -#from mock import MagicMock from couchpotato.core.plugins.browser import FileBrowser from couchpotato.core.softchroot import SoftChroot +import mock +import unittest +from unittest import TestCase + CHROOT_DIR = '/tmp/' class FileBrowserChrootedTest(TestCase): diff --git a/couchpotato/core/softchroot.py b/couchpotato/core/softchroot.py index 081299a..ca867e9 100644 --- a/couchpotato/core/softchroot.py +++ b/couchpotato/core/softchroot.py @@ -42,7 +42,7 @@ class SoftChroot: return self.chdir if not path.startswith(os.path.sep): - raise ValueError("path must starts with '/'") + raise ValueError("path must starts with '/' : %s" % path) return self.chdir[:-1] + path @@ -57,7 +57,7 @@ class SoftChroot: return '/' if not path.startswith(self.chdir): - raise ValueError("path must starts with 'chdir'") + raise ValueError("path must starts with 'chdir': %s" % path) l = len(self.chdir)-1 From 1b86822dee7c50ce5e3da2dff8e8f30301a87276 Mon Sep 17 00:00:00 2001 From: maxkoryukov Date: Thu, 14 Jan 2016 08:20:23 +0500 Subject: [PATCH 3/6] Rename test files --- couchpotato/core/plugins/browser_test.py | 50 +++++++++++++++++++++++++++++ couchpotato/core/plugins/test_browser.py | 50 ----------------------------- couchpotato/core/softchroot_test.py | 54 ++++++++++++++++++++++++++++++++ couchpotato/core/test_softchroot.py | 54 -------------------------------- 4 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 couchpotato/core/plugins/browser_test.py delete mode 100644 couchpotato/core/plugins/test_browser.py create mode 100644 couchpotato/core/softchroot_test.py delete mode 100644 couchpotato/core/test_softchroot.py diff --git a/couchpotato/core/plugins/browser_test.py b/couchpotato/core/plugins/browser_test.py new file mode 100644 index 0000000..fab63b5 --- /dev/null +++ b/couchpotato/core/plugins/browser_test.py @@ -0,0 +1,50 @@ +import sys +import os +import logging + +from couchpotato.core.plugins.browser import FileBrowser +from couchpotato.core.softchroot import SoftChroot + +#import mock +import unittest +from unittest import TestCase + +CHROOT_DIR = '/tmp/' + +class FileBrowserChrootedTest(TestCase): + def setUp(self): + self.b = FileBrowser() + + # TODO : remove scrutch: + self.b.soft_chroot = SoftChroot(CHROOT_DIR) + + # Logger + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + # To screen + hdlr = logging.StreamHandler(sys.stderr) + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%m-%d %H:%M:%S') + hdlr.setFormatter(formatter) + #logger.addHandler(hdlr) + + def test_soft_chroot_enabled(self): + self.assertTrue( self.b.soft_chroot.enabled) + + def test_view__chrooted_path_none(self): + #def view(self, path = '/', show_hidden = True, **kwargs): + r = self.b.view(None) + self.assertEqual(r['home'], '/') + self.assertEqual(r['parent'], '/') + self.assertTrue(r['is_root']) + + def test_view__chrooted_path_chroot(self): + #def view(self, path = '/', show_hidden = True, **kwargs): + for path, parent in [('/asdf','/'), (CHROOT_DIR, '/'), ('/mnk/123/t', '/mnk/123/')]: + r = self.b.view(path) + path_strip = path + if (path.endswith(os.path.sep)): + path_strip = path_strip.rstrip(os.path.sep) + + self.assertEqual(r['home'], '/') + self.assertEqual(r['parent'], parent) + self.assertFalse(r['is_root']) diff --git a/couchpotato/core/plugins/test_browser.py b/couchpotato/core/plugins/test_browser.py deleted file mode 100644 index 862a307..0000000 --- a/couchpotato/core/plugins/test_browser.py +++ /dev/null @@ -1,50 +0,0 @@ -import sys -import os -import logging - -from couchpotato.core.plugins.browser import FileBrowser -from couchpotato.core.softchroot import SoftChroot - -import mock -import unittest -from unittest import TestCase - -CHROOT_DIR = '/tmp/' - -class FileBrowserChrootedTest(TestCase): - def setUp(self): - self.b = FileBrowser() - - # TODO : remove scrutch: - self.b.soft_chroot = SoftChroot(CHROOT_DIR) - - # Logger - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - # To screen - hdlr = logging.StreamHandler(sys.stderr) - formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%m-%d %H:%M:%S') - hdlr.setFormatter(formatter) - #logger.addHandler(hdlr) - - def test_soft_chroot_enabled(self): - self.assertTrue( self.b.soft_chroot.enabled) - - def test_view__chrooted_path_none(self): - #def view(self, path = '/', show_hidden = True, **kwargs): - r = self.b.view(None) - self.assertEqual(r['home'], '/') - self.assertEqual(r['parent'], '/') - self.assertTrue(r['is_root']) - - def test_view__chrooted_path_chroot(self): - #def view(self, path = '/', show_hidden = True, **kwargs): - for path, parent in [('/asdf','/'), (CHROOT_DIR, '/'), ('/mnk/123/t', '/mnk/123/')]: - r = self.b.view(path) - path_strip = path - if (path.endswith(os.path.sep)): - path_strip = path_strip.rstrip(os.path.sep) - - self.assertEqual(r['home'], '/') - self.assertEqual(r['parent'], parent) - self.assertFalse(r['is_root']) diff --git a/couchpotato/core/softchroot_test.py b/couchpotato/core/softchroot_test.py new file mode 100644 index 0000000..ec515eb --- /dev/null +++ b/couchpotato/core/softchroot_test.py @@ -0,0 +1,54 @@ +import sys +import os +import logging +import unittest +from unittest import TestCase +#from mock import MagicMock + +from couchpotato.core.softchroot import SoftChroot + +CHROOT_DIR = '/tmp/' + +class SoftChrootEnabledTest(TestCase): + def setUp(self): + self.b = SoftChroot(CHROOT_DIR) + + def test_enabled(self): + self.assertTrue( self.b.enabled) + + def test_is_subdir(self): + self.assertFalse( self.b.is_subdir('') ) + self.assertFalse( self.b.is_subdir(None) ) + + self.assertTrue( self.b.is_subdir(CHROOT_DIR) ) + noslash = CHROOT_DIR[:-1] + self.assertTrue( self.b.is_subdir(noslash) ) + + self.assertTrue( self.b.is_subdir(CHROOT_DIR + 'come') ) + + def test_is_root_abs(self): + self.assertFalse( self.b.is_root_abs('') ) + self.assertFalse( self.b.is_root_abs(None) ) + + self.assertTrue( self.b.is_root_abs(CHROOT_DIR) ) + noslash = CHROOT_DIR[:-1] + self.assertTrue( self.b.is_root_abs(noslash) ) + + self.assertFalse( self.b.is_root_abs(CHROOT_DIR + 'come') ) + + def test_add(self): + with self.assertRaises(ValueError): + self.b.add('no_leading_slash') + + self.assertEqual( self.b.add(None), CHROOT_DIR ) + self.assertEqual( self.b.add(''), CHROOT_DIR ) + + self.assertEqual( self.b.add('/asdf'), CHROOT_DIR + 'asdf' ) + + def test_cut(self): + with self.assertRaises(ValueError): self.b.cut(None) + with self.assertRaises(ValueError): self.b.cut('') + + self.assertEqual( self.b.cut(CHROOT_DIR + 'asdf'), '/asdf' ) + self.assertEqual( self.b.cut(CHROOT_DIR), '/' ) + self.assertEqual( self.b.cut(CHROOT_DIR.rstrip(os.path.sep)), '/' ) diff --git a/couchpotato/core/test_softchroot.py b/couchpotato/core/test_softchroot.py deleted file mode 100644 index ec515eb..0000000 --- a/couchpotato/core/test_softchroot.py +++ /dev/null @@ -1,54 +0,0 @@ -import sys -import os -import logging -import unittest -from unittest import TestCase -#from mock import MagicMock - -from couchpotato.core.softchroot import SoftChroot - -CHROOT_DIR = '/tmp/' - -class SoftChrootEnabledTest(TestCase): - def setUp(self): - self.b = SoftChroot(CHROOT_DIR) - - def test_enabled(self): - self.assertTrue( self.b.enabled) - - def test_is_subdir(self): - self.assertFalse( self.b.is_subdir('') ) - self.assertFalse( self.b.is_subdir(None) ) - - self.assertTrue( self.b.is_subdir(CHROOT_DIR) ) - noslash = CHROOT_DIR[:-1] - self.assertTrue( self.b.is_subdir(noslash) ) - - self.assertTrue( self.b.is_subdir(CHROOT_DIR + 'come') ) - - def test_is_root_abs(self): - self.assertFalse( self.b.is_root_abs('') ) - self.assertFalse( self.b.is_root_abs(None) ) - - self.assertTrue( self.b.is_root_abs(CHROOT_DIR) ) - noslash = CHROOT_DIR[:-1] - self.assertTrue( self.b.is_root_abs(noslash) ) - - self.assertFalse( self.b.is_root_abs(CHROOT_DIR + 'come') ) - - def test_add(self): - with self.assertRaises(ValueError): - self.b.add('no_leading_slash') - - self.assertEqual( self.b.add(None), CHROOT_DIR ) - self.assertEqual( self.b.add(''), CHROOT_DIR ) - - self.assertEqual( self.b.add('/asdf'), CHROOT_DIR + 'asdf' ) - - def test_cut(self): - with self.assertRaises(ValueError): self.b.cut(None) - with self.assertRaises(ValueError): self.b.cut('') - - self.assertEqual( self.b.cut(CHROOT_DIR + 'asdf'), '/asdf' ) - self.assertEqual( self.b.cut(CHROOT_DIR), '/' ) - self.assertEqual( self.b.cut(CHROOT_DIR.rstrip(os.path.sep)), '/' ) From d24b01c17f1301f36ec507afeb9ec4aae75d4b8d Mon Sep 17 00:00:00 2001 From: maxkoryukov Date: Thu, 14 Jan 2016 17:10:11 +0500 Subject: [PATCH 4/6] SoftChroot: Huge pack of improvement and bugfixes * Improved SoftChroot (now it is a module, like a settings.py, included in Env) * skipping _test modules during plugins loading * hidden-opt meta could be loaded from config in python modules ( see _core.py->config ) + mock testing + a few tests for Env + list of python dev-requirements in a file fix maxkoryukov/CouchPotatoServer#8 --- .travis.yml | 3 + Gruntfile.js | 4 +- couchpotato/core/_base/_core.py | 3 +- couchpotato/core/loader.py | 5 ++ couchpotato/core/plugins/browser.py | 35 +++++---- couchpotato/core/plugins/browser_test.py | 42 ++++++----- couchpotato/core/settings.py | 38 +++++----- couchpotato/core/softchroot.py | 124 ++++++++++++++++++++++++------- couchpotato/core/softchroot_test.py | 95 +++++++++++++++++++---- couchpotato/environment.py | 2 + couchpotato/environment_test.py | 19 +++++ couchpotato/runner.py | 16 ++-- py-dev-requirements.txt | 5 ++ 13 files changed, 283 insertions(+), 108 deletions(-) create mode 100644 couchpotato/environment_test.py create mode 100644 py-dev-requirements.txt diff --git a/.travis.yml b/.travis.yml index 43f3b09..9d2de8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ cache: pip: true directories: - node_modules + - libs # command to install dependencies install: @@ -25,6 +26,8 @@ install: - pip install --upgrade nose # - pip install rednose + - nosetests --plugins + # command to run tests script: - grunt test diff --git a/Gruntfile.js b/Gruntfile.js index 0d57c47..41db92e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -72,7 +72,7 @@ module.exports = function(grunt){ options: { compass: true, update: true, - //sourcemap: 'none' + sourcemap: 'none' }, server: { files: [{ @@ -201,7 +201,7 @@ module.exports = function(grunt){ config: './.nosetestsrc', // 'rednose' is a colored output for nose test-runner. But we do not requre colors on travis-ci rednose: config.colorful_tests_output, - externalNose: true + externalNose: true, }, test: { diff --git a/couchpotato/core/_base/_core.py b/couchpotato/core/_base/_core.py index 62bf800..c740cf6 100644 --- a/couchpotato/core/_base/_core.py +++ b/couchpotato/core/_base/_core.py @@ -249,6 +249,7 @@ config = [{ { 'name': 'username', 'default': '', + 'ui-meta' : 'rw', }, { 'name': 'password', @@ -302,7 +303,7 @@ config = [{ { 'name': 'api_key', 'default': uuid4().hex, - 'readonly': 1, + 'ui-meta' : 'ro', 'description': 'Let 3rd party app do stuff. Docs', }, { diff --git a/couchpotato/core/loader.py b/couchpotato/core/loader.py index 11df37b..5b0f6fc 100644 --- a/couchpotato/core/loader.py +++ b/couchpotato/core/loader.py @@ -99,6 +99,11 @@ class Loader(object): path = os.path.join(dir_name, name) ext = os.path.splitext(path)[1] ext_length = len(ext) + + # SKIP test files: + if path.endswith('_test.py'): + continue + if name != 'static' and ((os.path.isdir(path) and os.path.isfile(os.path.join(path, '__init__.py'))) or (os.path.isfile(path) and ext == '.py')): name = name[:-ext_length] if ext_length > 0 else name diff --git a/couchpotato/core/plugins/browser.py b/couchpotato/core/plugins/browser.py index 599a02d..14b48f3 100644 --- a/couchpotato/core/plugins/browser.py +++ b/couchpotato/core/plugins/browser.py @@ -10,10 +10,10 @@ from couchpotato.core.event import addEvent from couchpotato.core.helpers.encoding import sp, ss, toUnicode from couchpotato.core.helpers.variable import getUserDir from couchpotato.core.plugins.base import Plugin -from couchpotato.core.softchroot import SoftChroot -log = CPLog(__name__) +from couchpotato.environment import Env +log = CPLog(__name__) if os.name == 'nt': import imp @@ -29,13 +29,9 @@ if os.name == 'nt': autoload = 'FileBrowser' - class FileBrowser(Plugin): def __init__(self): - soft_chroot_dir = self.conf('soft_chroot', section='core') - self.soft_chroot = SoftChroot(soft_chroot_dir) - addApiView('directory.list', self.view, docs = { 'desc': 'Return the directory list of a given directory', 'params': { @@ -81,17 +77,20 @@ class FileBrowser(Plugin): return driveletters def view(self, path = '/', show_hidden = True, **kwargs): + + soft_chroot = Env.get('softchroot') + home = getUserDir() - if self.soft_chroot.enabled: - if not self.soft_chroot.is_subdir(home): - home = self.soft_chroot.chdir + if soft_chroot.enabled: + if not soft_chroot.is_subdir(home): + home = soft_chroot.get_chroot() if not path: path = home if path.endswith(os.path.sep): path = path.rstrip(os.path.sep) - elif self.soft_chroot.enabled: - path = self.soft_chroot.add(path) + else: + path = soft_chroot.chroot2abs(path) try: dirs = self.getDirectories(path = path, show_hidden = show_hidden) @@ -99,8 +98,8 @@ class FileBrowser(Plugin): log.error('Failed getting directory "%s" : %s', (path, traceback.format_exc())) dirs = [] - if self.soft_chroot.enabled: - dirs = map(self.soft_chroot.cut, dirs) + if soft_chroot.enabled: + dirs = map(soft_chroot.abs2chroot, dirs) parent = os.path.dirname(path.rstrip(os.path.sep)) if parent == path.rstrip(os.path.sep): @@ -111,16 +110,16 @@ class FileBrowser(Plugin): # TODO : check on windows: is_root = path == '/' - if self.soft_chroot.enabled: - is_root = self.soft_chroot.is_root_abs(path) + if soft_chroot.enabled: + is_root = soft_chroot.is_root_abs(path) # fix paths: - if self.soft_chroot.is_subdir(parent): - parent = self.soft_chroot.cut(parent) + if soft_chroot.is_subdir(parent): + parent = soft_chroot.abs2chroot(parent) else: parent = os.path.sep - home = self.soft_chroot.cut(home) + home = soft_chroot.abs2chroot(home) return { 'is_root': is_root, diff --git a/couchpotato/core/plugins/browser_test.py b/couchpotato/core/plugins/browser_test.py index fab63b5..d1ac3bc 100644 --- a/couchpotato/core/plugins/browser_test.py +++ b/couchpotato/core/plugins/browser_test.py @@ -1,44 +1,46 @@ -import sys +#import sys import os -import logging + +import mock +import unittest +from unittest import TestCase + from couchpotato.core.plugins.browser import FileBrowser from couchpotato.core.softchroot import SoftChroot -#import mock -import unittest -from unittest import TestCase CHROOT_DIR = '/tmp/' +# 'couchpotato.core.plugins.browser.Env', +@mock.patch('couchpotato.core.plugins.browser.Env', name='EnvMock') class FileBrowserChrootedTest(TestCase): + def setUp(self): self.b = FileBrowser() - # TODO : remove scrutch: - self.b.soft_chroot = SoftChroot(CHROOT_DIR) - - # Logger - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - # To screen - hdlr = logging.StreamHandler(sys.stderr) - formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', '%m-%d %H:%M:%S') - hdlr.setFormatter(formatter) - #logger.addHandler(hdlr) + def tuneMock(self, env): + #set up mock: + sc = SoftChroot() + sc.initialize(CHROOT_DIR) + env.get.return_value = sc - def test_soft_chroot_enabled(self): - self.assertTrue( self.b.soft_chroot.enabled) - def test_view__chrooted_path_none(self): + def test_view__chrooted_path_none(self, env): #def view(self, path = '/', show_hidden = True, **kwargs): + + self.tuneMock(env) + r = self.b.view(None) self.assertEqual(r['home'], '/') self.assertEqual(r['parent'], '/') self.assertTrue(r['is_root']) - def test_view__chrooted_path_chroot(self): + def test_view__chrooted_path_chroot(self, env): #def view(self, path = '/', show_hidden = True, **kwargs): + + self.tuneMock(env) + for path, parent in [('/asdf','/'), (CHROOT_DIR, '/'), ('/mnk/123/t', '/mnk/123/')]: r = self.b.view(path) path_strip = path diff --git a/couchpotato/core/settings.py b/couchpotato/core/settings.py index ff923a8..85316e0 100644 --- a/couchpotato/core/settings.py +++ b/couchpotato/core/settings.py @@ -7,7 +7,6 @@ from couchpotato.api import addApiView from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.helpers.variable import mergeDicts, tryInt, tryFloat -from couchpotato.core.softchroot import SoftChroot class Settings(object): @@ -58,7 +57,6 @@ class Settings(object): self.file = None self.p = None self.log = None - self.soft_chroot = None self.directories_delimiter = "::" def setFile(self, config_file): @@ -95,6 +93,15 @@ class Settings(object): for option_name, option in options.items(): self.setDefault(section_name, option_name, option.get('default', '')) + # Set UI-meta for option (hidden/ro/rw) + if option.get('ui-meta'): + value = option.get('ui-meta') + if value: + value = value.lower() + if value in ['hidden', 'rw', 'ro']: + meta_option_name = option_name + self.optionMetaSuffix() + self.setDefault(section_name, meta_option_name, value) + # Migrate old settings from old location to the new location if option.get('migrate_from'): if self.p.has_option(option.get('migrate_from'), option_name): @@ -108,13 +115,6 @@ class Settings(object): if save: self.save() - def getSoftChroot(self): - if not self.soft_chroot: - soft_chroot_dir = self.get('soft_chroot', default = None) - self.soft_chroot = SoftChroot(soft_chroot_dir) - - return self.soft_chroot - def set(self, section, option, value): if not self.isOptionWritable(section, option): self.log.warning('set::option "%s.%s" isn\'t writable', (section, option)) @@ -185,7 +185,10 @@ class Settings(object): return toUnicode(value).strip() def getValues(self): + from couchpotato.environment import Env + values = {} + soft_chroot = Env.get('softchroot') # TODO : There is two commented "continue" blocks (# COMMENTED_SKIPPING). They both are good... # ... but, they omit output of values of hidden and non-readable options @@ -216,15 +219,15 @@ class Settings(object): if is_password and value: value = len(value) * '*' - # chrotify directory before sending to UI: + # chrootify directory before sending to UI: if (self.getType(section, option_name) == 'directory') and value: - try: value = self.getSoftChroot().cut(str(value)) + try: value = soft_chroot.abs2chroot(value) except: value = "" - # chrotify directories before sending to UI: + # chrootify directories before sending to UI: if (self.getType(section, option_name) == 'directories'): if (not value): value = [] - try : value = map(lambda xx : self.getSoftChroot().cut(str(xx)), value) + try : value = map(soft_chroot.abs2chroot, value) except : value = [] values[section][option_name] = value @@ -235,8 +238,6 @@ class Settings(object): with open(self.file, 'wb') as configfile: self.p.write(configfile) - self.log.debug('Saved settings') - def addSection(self, section): if not self.p.has_section(section): self.p.add_section(section) @@ -323,17 +324,18 @@ class Settings(object): option = kwargs.get('name') value = kwargs.get('value') - self.log.debug("value-> %s", (value)); + from couchpotato.environment import Env + soft_chroot = Env.get('softchroot') if self.getType(section, option) == 'directory': - value = self.getSoftChroot().add(str(value)) + value = soft_chroot.chroot2abs(value) if self.getType(section, option) == 'directories': import json value = json.loads(value) if not (value and isinstance(value, list)): value = [] - value = map(lambda xx : self.getSoftChroot().add(str(xx)), value) + value = map(soft_chroot.chroot2abs, value) value = self.directories_delimiter.join(value) # See if a value handler is attached, use that as value diff --git a/couchpotato/core/softchroot.py b/couchpotato/core/softchroot.py index ca867e9..ef05ef4 100644 --- a/couchpotato/core/softchroot.py +++ b/couchpotato/core/softchroot.py @@ -2,39 +2,99 @@ import os import sys +class SoftChrootInitError(IOError): + """Error during soft-chroot initialization""" + pass + class SoftChroot: - def __init__(self, chdir): - self.enabled = False + """Soft Chroot module + + Provides chroot feature for interation with Web-UI. Since it is not real chroot, so the name is SOFT CHROOT. + The module prevents access to entire file-system, allowing access only to subdirs of SOFT-CHROOT directory. + """ + def __init__(self): + self.enabled = None + self.chdir = None + + def initialize(self, chdir): + """ initialize module, by setting soft-chroot-directory + + Sets soft-chroot directory and 'enabled'-flag - self.chdir = chdir + Args: + self (SoftChroot) : self + chdir (string) : absolute path to soft-chroot - if None != self.chdir: - self.chdir = self.chdir.strip() - self.chdir = self.chdir.rstrip(os.path.sep) + os.path.sep + Raises: + SoftChrootInitError: when chdir doesn't exist + """ + + orig_chdir = chdir + + if chdir: + chdir = chdir.strip() + + if (chdir): + # enabling soft-chroot: + if not os.path.isdir(chdir): + raise SoftChrootInitError(2, 'SOFT-CHROOT is requested, but the folder doesn\'t exist', orig_chdir) + self.enabled = True + self.chdir = chdir.rstrip(os.path.sep) + os.path.sep + else: + self.enabled = False + + def get_chroot(self): + """Returns root in chrooted environment + + Raises: + RuntimeError: when `SoftChroot` is not initialized OR enabled + """ + if None == self.enabled: + raise RuntimeError('SoftChroot is not initialized') + if not self.enabled: + raise RuntimeError('SoftChroot is not enabled') + + return self.chdir def is_root_abs(self, abspath): - if not self.enabled: - raise Exception('chroot disabled') + """ Checks whether absolute path @abspath is the root in the soft-chrooted environment""" + if None == self.enabled: + raise RuntimeError('SoftChroot is not initialized') + if None == abspath: - return False - + raise ValueError('abspath can not be None') + + if not self.enabled: + # if not chroot environment : check, whether parent is the same dir: + parent = os.path.dirname(abspath.rstrip(os.path.sep)) + return parent==abspath + + # in soft-chrooted env: check, that path == chroot path = abspath.rstrip(os.path.sep) + os.path.sep return self.chdir == path - def is_subdir(self, path): + def is_subdir(self, abspath): + """ Checks whether @abspath is subdir (on any level) of soft-chroot""" + if None == self.enabled: + raise RuntimeError('SoftChroot is not initialized') + + if None == abspath: + return False + if not self.enabled: return True - if None == path: - return False + if not abspath.endswith(os.path.sep): + abspath += os.path.sep - if not path.endswith(os.path.sep): - path += os.path.sep + return abspath.startswith(self.chdir) - return path.startswith(self.chdir) + def chroot2abs(self, path): + """ Converts chrooted path to absolute path""" - def add(self, path): + if None == self.enabled: + raise RuntimeError('SoftChroot is not initialized') if not self.enabled: return path @@ -42,23 +102,33 @@ class SoftChroot: return self.chdir if not path.startswith(os.path.sep): - raise ValueError("path must starts with '/' : %s" % path) + path = os.path.sep + path return self.chdir[:-1] + path - def cut(self, path): - if not self.enabled: - return path + def abs2chroot(self, path, force = False): + """ Converts absolute path to chrooted path""" + + if None == self.enabled: + raise RuntimeError('SoftChroot is not initialized') - if None == path or 0==len(path): + if None == path: raise ValueError('path is empty') + if not self.enabled: + return path + if path == self.chdir.rstrip(os.path.sep): return '/' + resulst = None if not path.startswith(self.chdir): - raise ValueError("path must starts with 'chdir': %s" % path) - - l = len(self.chdir)-1 - - return path[l:] + if (force): + result = self.get_chroot() + else: + raise ValueError("path must starts with 'chdir': %s" % path) + else: + l = len(self.chdir)-1 + result = path[l:] + + return result diff --git a/couchpotato/core/softchroot_test.py b/couchpotato/core/softchroot_test.py index ec515eb..d5618cb 100644 --- a/couchpotato/core/softchroot_test.py +++ b/couchpotato/core/softchroot_test.py @@ -9,9 +9,65 @@ from couchpotato.core.softchroot import SoftChroot CHROOT_DIR = '/tmp/' +class SoftChrootNonInitialized(TestCase): + def setUp(self): + self.b = SoftChroot() + + def test_is_root_abs(self): + with self.assertRaises(RuntimeError): + self.b.is_root_abs('1') + + def test_is_subdir(self): + with self.assertRaises(RuntimeError): + self.b.is_subdir('1') + + def test_chroot2abs(self): + with self.assertRaises(RuntimeError): + self.b.chroot2abs('1') + + def test_abs2chroot(self): + with self.assertRaises(RuntimeError): + self.b.abs2chroot('1') + + def test_get_root(self): + with self.assertRaises(RuntimeError): + self.b.get_chroot() + +class SoftChrootNOTEnabledTest(TestCase): + def setUp(self): + self.b = SoftChroot() + self.b.initialize(None) + + def test_get_root(self): + with self.assertRaises(RuntimeError): + self.b.get_chroot() + + def test_chroot2abs_noleading_slash(self): + path = 'no_leading_slash' + self.assertEqual( self.b.chroot2abs(path), path ) + + def test_chroot2abs(self): + self.assertIsNone( self.b.chroot2abs(None), None ) + self.assertEqual( self.b.chroot2abs(''), '' ) + self.assertEqual( self.b.chroot2abs('/asdf'), '/asdf' ) + + def test_abs2chroot_raise_on_empty(self): + with self.assertRaises(ValueError): + self.b.abs2chroot(None) + + def test_abs2chroot(self): + self.assertEqual( self.b.abs2chroot(''), '' ) + self.assertEqual( self.b.abs2chroot('/asdf'), '/asdf' ) + self.assertEqual( self.b.abs2chroot('/'), '/' ) + + def test_get_root(self): + with self.assertRaises(RuntimeError): + self.b.get_chroot() + class SoftChrootEnabledTest(TestCase): def setUp(self): - self.b = SoftChroot(CHROOT_DIR) + self.b = SoftChroot() + self.b.initialize(CHROOT_DIR) def test_enabled(self): self.assertTrue( self.b.enabled) @@ -26,9 +82,12 @@ class SoftChrootEnabledTest(TestCase): self.assertTrue( self.b.is_subdir(CHROOT_DIR + 'come') ) + def test_is_root_abs_none(self): + with self.assertRaises(ValueError): + self.assertFalse( self.b.is_root_abs(None) ) + def test_is_root_abs(self): self.assertFalse( self.b.is_root_abs('') ) - self.assertFalse( self.b.is_root_abs(None) ) self.assertTrue( self.b.is_root_abs(CHROOT_DIR) ) noslash = CHROOT_DIR[:-1] @@ -36,19 +95,27 @@ class SoftChrootEnabledTest(TestCase): self.assertFalse( self.b.is_root_abs(CHROOT_DIR + 'come') ) - def test_add(self): - with self.assertRaises(ValueError): - self.b.add('no_leading_slash') + def test_chroot2abs_noleading_slash(self): + path = 'no_leading_slash' + path_sl = CHROOT_DIR + path + #with self.assertRaises(ValueError): + # self.b.chroot2abs('no_leading_slash') + self.assertEqual( self.b.chroot2abs(path), path_sl ) + + def test_chroot2abs(self): + self.assertEqual( self.b.chroot2abs(None), CHROOT_DIR ) + self.assertEqual( self.b.chroot2abs(''), CHROOT_DIR ) - self.assertEqual( self.b.add(None), CHROOT_DIR ) - self.assertEqual( self.b.add(''), CHROOT_DIR ) + self.assertEqual( self.b.chroot2abs('/asdf'), CHROOT_DIR + 'asdf' ) - self.assertEqual( self.b.add('/asdf'), CHROOT_DIR + 'asdf' ) + def test_abs2chroot_raise_on_empty(self): + with self.assertRaises(ValueError): self.b.abs2chroot(None) + with self.assertRaises(ValueError): self.b.abs2chroot('') - def test_cut(self): - with self.assertRaises(ValueError): self.b.cut(None) - with self.assertRaises(ValueError): self.b.cut('') + def test_abs2chroot(self): + self.assertEqual( self.b.abs2chroot(CHROOT_DIR + 'asdf'), '/asdf' ) + self.assertEqual( self.b.abs2chroot(CHROOT_DIR), '/' ) + self.assertEqual( self.b.abs2chroot(CHROOT_DIR.rstrip(os.path.sep)), '/' ) - self.assertEqual( self.b.cut(CHROOT_DIR + 'asdf'), '/asdf' ) - self.assertEqual( self.b.cut(CHROOT_DIR), '/' ) - self.assertEqual( self.b.cut(CHROOT_DIR.rstrip(os.path.sep)), '/' ) + def test_get_root(self): + self.assertEqual( self.b.get_chroot(), CHROOT_DIR ) diff --git a/couchpotato/environment.py b/couchpotato/environment.py index 1000d48..96011ab 100644 --- a/couchpotato/environment.py +++ b/couchpotato/environment.py @@ -5,6 +5,7 @@ from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.loader import Loader from couchpotato.core.settings import Settings +from couchpotato.core.softchroot import SoftChroot class Env(object): @@ -19,6 +20,7 @@ class Env(object): _settings = Settings() _database = Database() _loader = Loader() + _softchroot = SoftChroot() _cache = None _options = None _args = None diff --git a/couchpotato/environment_test.py b/couchpotato/environment_test.py new file mode 100644 index 0000000..bcf782c --- /dev/null +++ b/couchpotato/environment_test.py @@ -0,0 +1,19 @@ +import unittest +from unittest import TestCase +import mock + +from couchpotato.environment import Env + +class EnvironmentBaseTest(TestCase): + def test_appname(self): + self.assertEqual('CouchPotato', Env.get('appname')) + + def test_set_get_appname(self): + x = 'NEWVALUE' + Env.set('appname', x) + self.assertEqual(x, Env.get('appname')) + + def test_get_softchroot(self): + from couchpotato.core.softchroot import SoftChroot + sc = Env.get('softchroot') + self.assertIsInstance(sc, SoftChroot) diff --git a/couchpotato/runner.py b/couchpotato/runner.py index 1dacb7f..7a00016 100644 --- a/couchpotato/runner.py +++ b/couchpotato/runner.py @@ -23,7 +23,7 @@ import requests from requests.packages.urllib3 import disable_warnings from tornado.httpserver import HTTPServer from tornado.web import Application, StaticFileHandler, RedirectHandler - +from couchpotato.core.softchroot import SoftChrootInitError def getOptions(args): @@ -218,13 +218,13 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En # Check soft-chroot dir exists: try: - soft_chroot = Env.setting('soft_chroot', section = 'core', default = None, type='unicode' ) - - if (None != soft_chroot): - soft_chroot = soft_chroot.strip() - if (len(soft_chroot)>0) and (not os.path.isdir(soft_chroot)): - log.error('SOFT-CHROOT is defined, but the folder doesn\'t exist') - return + # Load Soft-Chroot + soft_chroot = Env.get('softchroot') + soft_chroot_dir = Env.setting('soft_chroot', section = 'core', default = None, type='unicode' ) + soft_chroot.initialize(soft_chroot_dir) + except SoftChrootInitError as exc: + log.error(exc) + return except: log.error('Unable to check whether SOFT-CHROOT is defined') return diff --git a/py-dev-requirements.txt b/py-dev-requirements.txt new file mode 100644 index 0000000..e0b0c4c --- /dev/null +++ b/py-dev-requirements.txt @@ -0,0 +1,5 @@ +nose +rednose +nose-htmloutput +coverage +coveralls From 93c6a02249cf712465737179158ada2f37a2c5b8 Mon Sep 17 00:00:00 2001 From: maxkoryukov Date: Thu, 14 Jan 2016 17:54:03 +0500 Subject: [PATCH 5/6] Style fix --- couchpotato/core/plugins/manage.py | 4 +- couchpotato/static/style/combined.min.css | 82 +++++++++++++++---------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/couchpotato/core/plugins/manage.py b/couchpotato/core/plugins/manage.py index b0e1239..eab5890 100755 --- a/couchpotato/core/plugins/manage.py +++ b/couchpotato/core/plugins/manage.py @@ -251,8 +251,8 @@ class Manage(Plugin): def directories(self): try: - if self.conf('library', default = '').strip(): - return splitString(self.conf('library', default = ''), '::') + if self.conf('library', default = ''): + return self.conf('library', default = '') except: pass diff --git a/couchpotato/static/style/combined.min.css b/couchpotato/static/style/combined.min.css index 2977344..cb41298 100644 --- a/couchpotato/static/style/combined.min.css +++ b/couchpotato/static/style/combined.min.css @@ -1,6 +1,6 @@ .page.movie_details,.thumb_list .movie.hover_start .actions{pointer-events:none} .movies>.description a:hover,.page.movie_details .releases .buttons a:hover{text-decoration:underline} -.update.message{background:#fff;padding:20px;text-align:center;font-size:1.25em} +.update.message{background:#FFF;padding:20px;text-align:center;font-size:1.25em} .dark .update.message{background:#2d2d2d} @media (max-width:768px){.update.message{font-size:1em} } @@ -14,7 +14,7 @@ .dark .search_form .wrapper{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} .search_form .wrapper:before{-webkit-transform:rotate(45deg);transform:rotate(45deg);content:'';display:block;position:absolute;height:10px;width:10px;background:#ac0000;left:-6px;bottom:16px;z-index:1} .dark .search_form .wrapper:before{background:#f85c22} -.search_form .input{background:#fff;border-radius:3px 0 0 3px;position:relative;left:4px;height:44px;overflow:hidden;width:100%} +.search_form .input{background:#FFF;border-radius:3px 0 0 3px;position:relative;left:4px;height:44px;overflow:hidden;width:100%} .dark .search_form .input{background:#2d2d2d} .search_form .input input{position:absolute;top:0;left:0;height:100%;width:100%;z-index:1} .search_form .input input::-ms-clear{width:0;height:0} @@ -29,7 +29,7 @@ .page.home .search_form,.search_form.focused,.search_form.shown{border-color:#04bce6} .page.home .search_form .wrapper,.search_form.focused .wrapper,.search_form.shown .wrapper{display:block;width:380px;-webkit-transform-origin:0 90%;transform-origin:0 90%} .page.home .search_form .input input,.search_form.focused .input input,.search_form.shown .input input{opacity:1} -.search_form .results_container{min-height:50px;text-align:left;position:relative;left:4px;display:none;background:#fff;border-radius:3px 0 0;overflow:hidden} +.search_form .results_container{min-height:50px;text-align:left;position:relative;left:4px;display:none;background:#FFF;border-radius:3px 0 0;overflow:hidden} .dark .search_form .results_container{background:#2d2d2d} .search_form .results_container .results{max-height:280px;overflow-x:hidden} .search_form .results_container .results .media_result{overflow:hidden;height:50px;position:relative} @@ -54,9 +54,9 @@ .search_form .results_container .results .media_result .options .add a{color:#FFF} .search_form .results_container .results .media_result .options .button{display:block;background:#ac0000;text-align:center;margin:0} .dark .search_form .results_container .results .media_result .options .button{background:#f85c22} -.search_form .results_container .results .media_result .options .message{font-size:20px;color:#fff} +.search_form .results_container .results .media_result .options .message{font-size:20px;color:#FFF} .search_form .results_container .results .media_result .thumbnail{width:30px;min-height:100%;display:block;margin:0;vertical-align:top} -.search_form .results_container .results .media_result .data{position:absolute;height:100%;top:0;left:30px;right:0;cursor:pointer;border-top:1px solid rgba(255,255,255,.08);transition:all .4s cubic-bezier(.9,0,.1,1);will-change:transform;-webkit-transform:translateX(0) rotateZ(360deg);transform:translateX(0) rotateZ(360deg);background:#fff} +.search_form .results_container .results .media_result .data{position:absolute;height:100%;top:0;left:30px;right:0;cursor:pointer;border-top:1px solid rgba(255,255,255,.08);transition:all .4s cubic-bezier(.9,0,.1,1);will-change:transform;-webkit-transform:translateX(0) rotateZ(360deg);transform:translateX(0) rotateZ(360deg);background:#FFF} .dark .search_form .results_container .results .media_result .data{background:#2d2d2d;border-color:rgba(255,255,255,.08)} @media (max-width:480px){.search_form .results_container .results .media_result .thumbnail{display:none} .search_form .results_container .results .media_result .data{left:0} @@ -79,7 +79,7 @@ } .page.home .search_form .icon-search{display:block;color:#000;right:20px;top:20px;width:66px;height:66px;line-height:66px;left:auto;-webkit-transform:none;transform:none;font-size:2em;opacity:.5} .page.home .search_form .wrapper:before,.page.movies .scroll_content{display:none} -.dark .page.home .search_form .icon-search{color:#fff} +.dark .page.home .search_form .icon-search{color:#FFF} .page.home .search_form .icon-search:hover{background:0 0} @media (max-width:480px){.page.home .search_form .icon-search{width:44px;height:44px;line-height:44px;right:10px;top:10px;font-size:1.5em} } @@ -87,7 +87,7 @@ @media (max-width:480px){.page.home .search_form .wrapper{right:10px;top:10px;left:10px} } .page.home .search_form .wrapper .input{border-radius:0;left:0;position:absolute;top:0;height:66px} -.page.home .search_form .wrapper .input input{box-shadow:0;font-size:2em;font-weight:400;padding-right:66px;background:#fff} +.page.home .search_form .wrapper .input input{box-shadow:0;font-size:2em;font-weight:400;padding-right:66px;background:#FFF} .dark .page.home .search_form .wrapper .input input{background:#2d2d2d} .page.home .search_form .wrapper .results_container{min-height:66px;position:absolute;top:66px;left:0;right:0;border:1px solid #ebebeb;border-top:0} .dark .page.home .search_form .wrapper .results_container{border-color:#353535} @@ -129,9 +129,9 @@ .movie .quality span{display:inline-block;background:#ebebeb;border:1px solid transparent;color:rgba(0,0,0,.5);border-radius:1px;padding:1px 3px} .dark .movie .quality span{color:rgba(255,255,255,.5)} .movie .quality span.failed{background:#993619;color:#FFF} -.movie .quality span.available{color:#009902;border-color:#009902;background:#fff} -.movie .quality span.snatched{background:#548399;color:#fff} -.movie .quality span.done,.movie .quality span.downloaded{background:#009902;color:#fff} +.movie .quality span.available{color:#009902;border-color:#009902;background:#FFF} +.movie .quality span.snatched{background:#548399;color:#FFF} +.movie .quality span.done,.movie .quality span.downloaded{background:#009902;color:#FFF} .dark .movie .quality span{background:0 0} .dark .movie .quality span.available{border-color:transparent;background:0 0} .dark .movie .quality span.snatched{background:#548399} @@ -155,11 +155,11 @@ .movies>.description{position:absolute;top:0;right:20px;width:auto;line-height:80px;opacity:.7} .movies>.description a{color:#ac0000;display:inline} .dark .movies>.description a{color:#f85c22} -.movies>.loading{background:#fff} +.movies>.loading{background:#FFF} .dark .movies>.loading{background:#2d2d2d} .movies>.loading .message{color:#000} -.dark .movies>.loading .message{color:#fff} -.movies>.loading .spinner{background-color:#fff} +.dark .movies>.loading .message{color:#FFF} +.movies>.loading .spinner{background-color:#FFF} .dark .movies>.loading .spinner{background-color:#2d2d2d} .movies .movie .actions{will-change:transform,opacity;-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)} .movies .progress div{width:50%;padding:5px 10px;display:-webkit-flex;display:-ms-flexbox;display:flex} @@ -190,11 +190,11 @@ .list_list .movie .info .rating .vote{display:inline-block;min-width:60px;text-align:right} .list_list .movie .actions{position:absolute;right:10px;top:0;bottom:0;display:none;z-index:10} .list_list .movie .actions .action{display:inline-block} -.list_list .movie .actions a{height:100%;display:block;background:#fff;color:#ac0000;padding:10px;width:auto;float:right;text:#000} +.list_list .movie .actions a{height:100%;display:block;background:#FFF;color:#ac0000;padding:10px;width:auto;float:right;text:#000} .list_list .movie .actions a .icon,.list_list .movie .actions a:before{display:none} -.dark .list_list .movie .actions a{background:#2d2d2d;color:#f85c22;text:#fff} +.dark .list_list .movie .actions a{background:#2d2d2d;color:#f85c22;text:#FFF} .list_list .movie .actions a:hover{background:#ebebeb;color:#000} -.dark .list_list .movie .actions a:hover{background:#353535;color:#fff} +.dark .list_list .movie .actions a:hover{background:#353535;color:#FFF} .list_list .movie:hover .actions{display:block} @media (max-width:768px){.list_list .movie .info .quality span{margin:2px 2px 0 0} .list_list .movie:hover .actions{display:none} @@ -266,7 +266,7 @@ .thumb_list .movie .actions .action{position:relative;margin-right:10px;float:right;clear:both} .thumb_list .movie .actions .action:first-child{margin-top:10px} .thumb_list .movie .actions .action a{transition:all 150ms cubic-bezier(.9,0,.1,1);will-change:color,background;transition-property:color,background;display:block;width:auto;padding:6.67px;color:#FFF;border-radius:2px;font-weight:400} -.thumb_list .movie .actions .action a:hover{background:#fff;color:#ac0000} +.thumb_list .movie .actions .action a:hover{background:#FFF;color:#ac0000} .dark .thumb_list .movie .actions .action a:hover{background:#2d2d2d;color:#FFF} .thumb_list .movie:hover .actions{opacity:1;visibility:visible} .thumb_list .movie .mask{bottom:44px;border-radius:3px;will-change:opacity;transition:opacity 30ms} @@ -279,7 +279,7 @@ } .page.movie_details .overlay .close{display:inline-block;text-align:center;font-size:60px;line-height:80px;color:#FFF;width:100%;height:100%;opacity:0;will-change:opacity;transition:opacity .3s ease .2s} .page.movie_details .overlay .close:before{display:block;width:44px} -.page.movie_details .scroll_content{position:fixed;z-index:2;top:0;bottom:0;right:0;left:176px;background:#fff;border-radius:3px 0 0 3px;overflow-y:auto;will-change:transform;-webkit-transform:translateX(100%) rotateZ(360deg);transform:translateX(100%) rotateZ(360deg);transition:-webkit-transform 450ms cubic-bezier(.9,0,.1,1);transition:transform 450ms cubic-bezier(.9,0,.1,1)} +.page.movie_details .scroll_content{position:fixed;z-index:2;top:0;bottom:0;right:0;left:176px;background:#FFF;border-radius:3px 0 0 3px;overflow-y:auto;will-change:transform;-webkit-transform:translateX(100%) rotateZ(360deg);transform:translateX(100%) rotateZ(360deg);transition:-webkit-transform 450ms cubic-bezier(.9,0,.1,1);transition:transform 450ms cubic-bezier(.9,0,.1,1)} .dark .page.movie_details .scroll_content{background:#2d2d2d} .page.movie_details .scroll_content>.head{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;padding:0 20px 0 10px;position:relative;z-index:2;will-change:transform,opacity;-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)} .page.movie_details .scroll_content>.head h1{-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;margin:0;font-size:24px;font-weight:300;max-width:100%} @@ -291,13 +291,13 @@ .page.movie_details .scroll_content>.head h1 .more_menu .icon-dropdown:before{right:10px} } .page.movie_details .scroll_content>.head h1 .more_menu a{color:#000} -.dark .page.movie_details .scroll_content>.head h1 .more_menu a{color:#fff} +.dark .page.movie_details .scroll_content>.head h1 .more_menu a{color:#FFF} .page.movie_details .scroll_content>.head h1 .more_menu .icon-dropdown{padding-right:30px} .page.movie_details .scroll_content>.head .more_menu{display:inline-block;vertical-align:top;max-width:100%;margin-bottom:0} .page.movie_details .scroll_content>.head .more_menu>a{float:left;line-height:80px;color:#ac0000} .dark .page.movie_details .scroll_content>.head .more_menu>a{color:#f85c22} .page.movie_details .scroll_content>.head .more_menu>a:hover{color:#000} -.dark .page.movie_details .scroll_content>.head .more_menu>a:hover{color:#fff} +.dark .page.movie_details .scroll_content>.head .more_menu>a:hover{color:#FFF} .page.movie_details .scroll_content>.head .more_menu .icon-dropdown{position:relative;padding:0 25px 0 10px} .page.movie_details .scroll_content>.head .more_menu .icon-dropdown:before{position:absolute;right:10px;top:-2px;opacity:.2} .page.movie_details .scroll_content>.head .more_menu .icon-dropdown:hover:before{opacity:1} @@ -318,7 +318,7 @@ .page.movie_details .scroll_content>.head .buttons>a{display:inline-block;padding:0 10px;color:#ac0000;line-height:80px} .dark .page.movie_details .scroll_content>.head .buttons>a{color:#f85c22} .page.movie_details .scroll_content>.head .buttons>a:hover{background:#ebebeb;color:#000} -.dark .page.movie_details .scroll_content>.head .buttons>a:hover{background:#353535;color:#fff} +.dark .page.movie_details .scroll_content>.head .buttons>a:hover{background:#353535;color:#FFF} .page.movie_details .scroll_content .section{padding:20px;border-top:1px solid rgba(0,0,0,.1);will-change:transform,opacity;-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)} .dark .page.movie_details .scroll_content .section{border-color:rgba(255,255,255,.1)} @media (max-width:480px){.page.movie_details .scroll_content>.head .more_menu.title .wrapper{top:30px;max-width:240px} @@ -341,7 +341,7 @@ .page.movie_details .section_add .options>div .title{min-width:75px;width:2000px;margin:0 10px 0 0} .page.movie_details .section_add .options>div .category,.page.movie_details .section_add .options>div .profile{width:200px;min-width:50px;margin:0 10px 0 0} .page.movie_details .section_add .options>div .add{width:200px} -.page.movie_details .section_add .options>div .add .button{background:#fff;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;display:block;text-align:center;width:100%;margin:0} +.page.movie_details .section_add .options>div .add .button{background:#FFF;-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;display:block;text-align:center;width:100%;margin:0} .dark .page.movie_details .section_add .options>div .add .button{background:#2d2d2d} .page.movie_details .section_add .options>div .add .button:hover{background:#ac0000} .dark .page.movie_details .section_add .options>div .add .button:hover{background:#f85c22} @@ -355,7 +355,7 @@ @media (max-width:480px){.page.movie_details .releases .item{display:block} } .page.movie_details .releases .item:not(.head):hover{background:#ebebeb;text:#000} -.dark .page.movie_details .releases .item:not(.head):hover{background:#353535;text:#fff} +.dark .page.movie_details .releases .item:not(.head):hover{background:#353535;text:#FFF} .page.movie_details .releases .item span{text-align:center} .page.movie_details .releases .item span:before{display:none;font-weight:700;opacity:.8;margin-right:3px;width:100%;font-size:.9em} .page.movie_details .releases .item .name{-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;text-align:left} @@ -370,7 +370,7 @@ .page.movie_details .releases .item .actions a{text-align:center} } .page.movie_details .releases .item .actions a{display:inline-block;vertical-align:top;padding:6.67px;min-width:26px;color:#000} -.dark .page.movie_details .releases .item .actions a{color:#fff} +.dark .page.movie_details .releases .item .actions a{color:#FFF} .page.movie_details .releases .item .actions a:hover{color:#ac0000} .dark .page.movie_details .releases .item .actions a:hover{color:#f85c22} .page.movie_details .releases .item .actions a:after{margin-left:3px;font-size:.9em} @@ -419,7 +419,7 @@ .page.movie_details .section_trailer.section_trailer .trailer_container:hover .icon-play,.page.movie_details .section_trailer.section_trailer .trailer_container:hover .icon-play span{opacity:1} .page.movie_details .section_trailer.section_trailer .trailer_container iframe{position:absolute;width:100%;height:100%;border:0;top:0;left:0;max-height:450px;z-index:10} .alph_nav{position:relative} -.alph_nav .mass_edit_form{display:-webkit-flex;display:-ms-flexbox;display:flex;background:#fff;position:fixed;top:80px;right:0;left:132px;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center;will-change:max-height;transition:max-height .3s cubic-bezier(.9,0,.1,1);max-height:0;overflow:hidden} +.alph_nav .mass_edit_form{display:-webkit-flex;display:-ms-flexbox;display:flex;background:#FFF;position:fixed;top:80px;right:0;left:132px;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center;will-change:max-height;transition:max-height .3s cubic-bezier(.9,0,.1,1);max-height:0;overflow:hidden} .dark .alph_nav .mass_edit_form{background:#2d2d2d} .mass_editing .alph_nav .mass_edit_form{max-height:44px} .alph_nav .mass_edit_form>*{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center} @@ -443,7 +443,7 @@ .alph_nav .menus .filter .button{margin-top:-2px} .alph_nav .menus .filter .search{position:relative} .alph_nav .menus .filter .search:before{position:absolute;height:100%;line-height:38px;padding-left:10px;font-size:16px;opacity:.5} -.alph_nav .menus .filter .search input{width:100%;padding:10px 10px 10px 30px;background:#fff;border:#ebebeb;border-bottom:1px solid transparent} +.alph_nav .menus .filter .search input{width:100%;padding:10px 10px 10px 30px;background:#FFF;border:#ebebeb;border-bottom:1px solid transparent} .dark .alph_nav .menus .filter .search input{background:#2d2d2d;border-color:#353535} .alph_nav .menus .filter .numbers{padding:10px} .alph_nav .menus .filter .numbers li{float:left;width:10%;height:30px;line-height:30px;text-align:center;opacity:.2;cursor:default;border:0} @@ -611,7 +611,7 @@ .api_docs .database table .form,.api_docs .database table form{width:600px} .api_docs .database table textarea{font-size:12px;width:100%;height:200px} .api_docs .database table input[type=submit]{display:block} -.page.login{background:#fff;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;font-size:1.25em} +.page.login{background:#FFF;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;font-size:1.25em} .dark .page.login{background:#2d2d2d} .page.login h1{padding:0 0 10px;font-size:60px;font-family:Lobster;font-weight:400;color:#ac0000;text-align:center} .dark .page.login h1{color:#f85c22} @@ -661,11 +661,11 @@ @font-face{font-family:Lobster;src:url(../fonts/Lobster-webfont.eot);src:url(../fonts/Lobster-webfont.eot?#iefix) format("embedded-opentype"),url(../fonts/Lobster-webfont.woff2) format("woff2"),url(../fonts/Lobster-webfont.woff) format("woff"),url(../fonts/Lobster-webfont.ttf) format("truetype"),url(../fonts/Lobster-webfont.svg#lobster_14regular) format("svg");font-weight:400;font-style:normal} *{margin:0;padding:0;box-sizing:border-box;text-rendering:optimizeSpeed;-webkit-backface-visibility:hidden;backface-visibility:hidden} body,html{font-size:14px;line-height:1.5;font-family:OpenSans,"Helvetica Neue",Helvetica,Arial,Geneva,sans-serif;font-weight:300;height:100%;margin:0;padding:0;background:#111;color:#000;overflow:hidden} -.dark body,.dark html{background:#111;color:#fff} +.dark body,.dark html{background:#111;color:#FFF} a{position:relative;overflow:hidden;text-decoration:none;cursor:pointer;-webkit-tap-highlight-color:transparent} a:active,a:visited{color:inherit} input,select,textarea{font-size:1em;font-weight:300;padding:6.67px;border-radius:0;border:1px solid #b8b8b8;background:#ebebeb;color:#000} -.dark input,.dark select,.dark textarea{border-color:#1b1b1b;background:#353535;color:#fff} +.dark input,.dark select,.dark textarea{border-color:#1b1b1b;background:#353535;color:#FFF} input[type=text],textarea{-webkit-appearance:none} .button{color:#ac0000;font-weight:300;padding:5px;cursor:pointer;border:1px solid #ac0000;border-radius:3px;margin:0 5px;transition:all 150ms} .dark .button{color:#f85c22;border-color:#f85c22} @@ -732,7 +732,7 @@ input[type=text],textarea{-webkit-appearance:none} .dark .corner_background{background:#f85c22} @media (max-width:480px){.corner_background{left:44px} } -.content{position:absolute;height:100%;top:0;left:132px;right:0;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:column nowrap;-ms-flex-flow:column nowrap;flex-flow:column nowrap;background:#fff;border-radius:3px 0 0 3px} +.content{position:absolute;height:100%;top:0;left:132px;right:0;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-flow:column nowrap;-ms-flex-flow:column nowrap;flex-flow:column nowrap;background:#FFF;border-radius:3px 0 0 3px} @media (max-width:480px){.content{left:44px} } .dark .content{background:#2d2d2d} @@ -745,7 +745,7 @@ input[type=text],textarea{-webkit-appearance:none} .page.home .scroll_content{padding:0 0 20px} .page h1,.page h2,.page h3,.page h4{font-weight:300} .page h2{font-size:24px;padding:20px} -.page .navigation{z-index:2;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:fixed;top:0;height:80px;left:152px;right:20px;background:#fff;border-radius:3px 0 0} +.page .navigation{z-index:2;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:fixed;top:0;height:80px;left:152px;right:20px;background:#FFF;border-radius:3px 0 0} .more_menu .button,.more_menu a,.page .navigation ul li{display:inline-block} .more_menu,.more_menu .button:before{position:relative} .dark .page .navigation{background:#2d2d2d} @@ -755,7 +755,7 @@ input[type=text],textarea{-webkit-appearance:none} .page .navigation ul{-webkit-flex:1 auto;-ms-flex:1 auto;flex:1 auto;list-style:none} .page .navigation ul li a{display:inline-block;font-size:24px;line-height:80px;padding:0 20px;color:#000;opacity:.5;vertical-align:bottom} .page .navigation ul .active a,.page .navigation ul li a:hover,.question.show{opacity:1} -.dark .page .navigation ul li a{color:#fff} +.dark .page .navigation ul li a{color:#FFF} .page .navigation>ul>li:first-child{margin-left:-20px} @media (max-width:480px){.page .navigation ul li a{font-size:18px;line-height:44px;padding:0 10px} .page .navigation>ul>li:first-child{margin-left:-10px} @@ -774,13 +774,13 @@ input[type=text],textarea{-webkit-appearance:none} .dark .more_menu .wrapper{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} .more_menu .wrapper:before{-webkit-transform:rotate(45deg) translateY(-60%);transform:rotate(45deg) translateY(-60%);content:'';display:block;position:absolute;background:#ac0000;height:10px;width:10px;left:-9px;bottom:11px;z-index:1;opacity:1;border-radius:3px} .dark .more_menu .wrapper:before{background:#f85c22} -.more_menu .wrapper ul{background:#fff;position:relative;z-index:2;overflow:hidden;border-radius:3px 0 0 3px} +.more_menu .wrapper ul{background:#FFF;position:relative;z-index:2;overflow:hidden;border-radius:3px 0 0 3px} .dark .more_menu .wrapper ul{background:#2d2d2d} .more_menu .wrapper ul li{display:block;line-height:1em;border-top:1px solid transparent;border-color:#ebebeb} .dark .more_menu .wrapper ul li{border-color:#353535} .more_menu .wrapper ul li:first-child{border-top:0} .more_menu .wrapper ul li a{display:block;color:#000;padding:5px 10px;font-size:1em;line-height:22px;width:100%} -.dark .more_menu .wrapper ul li a{color:#fff} +.dark .more_menu .wrapper ul li a{color:#FFF} .more_menu .wrapper ul li a:hover{background:#ebebeb} .dark .more_menu .wrapper ul li a:hover{background:#353535} .more_menu .wrapper ul li:first-child a{padding-top:10px} @@ -789,7 +789,7 @@ input[type=text],textarea{-webkit-appearance:none} .messages .message{overflow:hidden;transition:all .6s cubic-bezier(.9,0,.1,1);width:100%;position:relative;max-height:0;font-size:1.1em;font-weight:400;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:100% 50%;transform-origin:100% 50%;background:#ac0000;margin-bottom:4px;border-radius:3px;box-shadow:0 0 15px 2px rgba(0,0,0,.15)} .mask,.messages .close{position:absolute;top:0;right:0} .dark .messages .message{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} -.messages .message .inner{padding:15px 30px 15px 20px;background:#fff;margin-bottom:4px;border-radius:3px} +.messages .message .inner{padding:15px 30px 15px 20px;background:#FFF;margin-bottom:4px;border-radius:3px} .dark .messages .message .inner{background:#2d2d2d} .messages .message.sticky .inner{background-color:#ac0000} .dark .messages .message.sticky .inner{background-color:#f85c22} @@ -809,7 +809,7 @@ input[type=text],textarea{-webkit-appearance:none} .mask .message,.mask .spinner{position:absolute;top:50%;left:50%} .mask .message{color:#FFF;text-align:center;width:320px;margin:-49px 0 0 -160px;font-size:16px} .mask .message h1{font-size:1.5em} -.mask .spinner{width:22px;height:22px;display:block;background:#fff;margin-top:-11px;margin-left:-11px;outline:transparent solid 1px;-webkit-animation:rotating 2.5s cubic-bezier(.9,0,.1,1) infinite normal;animation:rotating 2.5s cubic-bezier(.9,0,.1,1) infinite normal;-webkit-transform:scale(0);transform:scale(0)} +.mask .spinner{width:22px;height:22px;display:block;background:#FFF;margin-top:-11px;margin-left:-11px;outline:transparent solid 1px;-webkit-animation:rotating 2.5s cubic-bezier(.9,0,.1,1) infinite normal;animation:rotating 2.5s cubic-bezier(.9,0,.1,1) infinite normal;-webkit-transform:scale(0);transform:scale(0)} .mask.with_message .spinner{margin-top:-88px} .mask.show{pointer-events:auto;opacity:1} .mask.show .spinner{-webkit-transform:scale(1);transform:scale(1)} @@ -847,7 +847,7 @@ input[type=text],textarea{-webkit-appearance:none} .page.settings .tabs li a{color:rgba(0,0,0,.5)} .dark .page.settings .tabs li a{color:rgba(255,255,255,.5)} .page.settings .tabs li.active a{color:#000} -.dark .page.settings .tabs li.active a{color:#fff} +.dark .page.settings .tabs li.active a{color:#FFF} .page.settings form.containers{margin:0 20px 0 0;-webkit-flex:1;-ms-flex:1;flex:1} @media (max-width:480px){.page.settings form.containers{margin:0 10px 0 0} } @@ -912,14 +912,14 @@ input[type=text],textarea{-webkit-appearance:none} .page.settings .switch{display:inline-block;background:#ac0000;height:20px;width:50px;min-width:0!important;transition:all 250ms;cursor:pointer;border-radius:20px} .dark .page.settings .switch{background:#f85c22} .page.settings .switch input{display:none} -.page.settings .switch .toggle{background:#fff;margin:1px;height:18px;width:18px;transition:-webkit-transform 250ms;transition:transform 250ms;-webkit-transform:translateX(30px);transform:translateX(30px);border-radius:20px} +.page.settings .switch .toggle{background:#FFF;margin:1px;height:18px;width:18px;transition:-webkit-transform 250ms;transition:transform 250ms;-webkit-transform:translateX(30px);transform:translateX(30px);border-radius:20px} .dark .page.settings .switch .toggle{background:#2d2d2d} .page.settings fieldset.enabler.disabled .switch,.page.settings:not(.show_advanced) .advanced_toggle .switch{background:#ebebeb;border-color:#ebebeb} .dark .page.settings fieldset.enabler.disabled .switch,.dark .page.settings:not(.show_advanced) .advanced_toggle .switch{background:#4e4e4e;border-color:#4e4e4e} .page.settings fieldset.enabler.disabled .switch .toggle,.page.settings:not(.show_advanced) .advanced_toggle .switch .toggle{-webkit-transform:translateX(0);transform:translateX(0)} .page.settings fieldset.enabler.disabled .switch:hover,.page.settings:not(.show_advanced) .advanced_toggle .switch:hover{background:#b8b8b8;border-color:#b8b8b8} .dark .page.settings fieldset.enabler.disabled .switch:hover,.dark .page.settings:not(.show_advanced) .advanced_toggle .switch:hover{background:#020202;border-color:#020202} -.page.settings .option_list{background:#fff;margin-top:10px} +.page.settings .option_list{background:#FFF;margin-top:10px} .dark .page.settings .option_list{background:#2d2d2d} .page.settings .option_list fieldset{position:relative} .page.settings .option_list fieldset h2 .group_label{min-width:100px} @@ -933,7 +933,7 @@ input[type=text],textarea{-webkit-appearance:none} .page.settings .option_list h2 .hint{font-weight:300} .page.settings .combined_table{margin-top:20px} .page.settings .combined_table .head{margin:0 10px 0 46px;font-size:.8em} -.page.settings .combined_table .head abbr{display:inline-block;font-weight:700;border-bottom:1px dotted #fff;line-height:140%;cursor:help;margin-right:10px;text-align:center} +.page.settings .combined_table .head abbr{display:inline-block;font-weight:700;border-bottom:1px dotted #FFF;line-height:140%;cursor:help;margin-right:10px;text-align:center} .page.settings .combined_table .head abbr:first-child{display:none} .page.settings .combined_table input{min-width:0!important;display:inline-block;margin-right:10px} .page.settings .combined_table .automation_ids,.page.settings .combined_table .automation_urls,.page.settings .combined_table .host{width:200px} @@ -986,7 +986,7 @@ input[type=text],textarea{-webkit-appearance:none} .dark .directory_list{background:#f85c22;box-shadow:0 5px 15px 2px rgba(0,0,0,.4)} .directory_list .pointer{border-right:6px solid transparent;border-left:6px solid transparent;border-bottom:6px solid transparent;border-bottom-color:#ac0000;display:block;position:absolute;width:0;margin:-6px 0 0 100px} .dark .directory_list .pointer{border-bottom-color:#f85c22} -.directory_list .wrapper{background:#fff;border-radius:3px 3px 0 0;margin-top:5px} +.directory_list .wrapper{background:#FFF;border-radius:3px 3px 0 0;margin-top:5px} .dark .directory_list .wrapper{background:#2d2d2d} .directory_list ul{width:92%;height:300px;overflow:auto;margin:0 20px} .directory_list li{padding:4px 20px 4px 0;cursor:pointer;margin:0!important;border-top:1px solid rgba(255,255,255,.1);overflow:hidden;white-space:nowrap;text-overflow:ellipsis} From 087b7641295ecc12c81dbcc721cd6c2d5feb273c Mon Sep 17 00:00:00 2001 From: maxkoryukov Date: Sat, 16 Jan 2016 14:10:31 +0500 Subject: [PATCH 6/6] patches for manage.py and settings.py settings - extended logging manage - correct default value for directories --- couchpotato/core/plugins/manage.py | 3 +-- couchpotato/core/settings.py | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/plugins/manage.py b/couchpotato/core/plugins/manage.py index eab5890..c639354 100755 --- a/couchpotato/core/plugins/manage.py +++ b/couchpotato/core/plugins/manage.py @@ -251,8 +251,7 @@ class Manage(Plugin): def directories(self): try: - if self.conf('library', default = ''): - return self.conf('library', default = '') + return self.conf('library', default = []) except: pass diff --git a/couchpotato/core/settings.py b/couchpotato/core/settings.py index 85316e0..e99c543 100644 --- a/couchpotato/core/settings.py +++ b/couchpotato/core/settings.py @@ -101,6 +101,8 @@ class Settings(object): if value in ['hidden', 'rw', 'ro']: meta_option_name = option_name + self.optionMetaSuffix() self.setDefault(section_name, meta_option_name, value) + else: + self.log.warning('Wrong value for option %s.%s : ui-meta can not be equal to "%s"', (section_name, option_name, value)) # Migrate old settings from old location to the new location if option.get('migrate_from'):