From 1743ce93f2223bc30a612a4226afb66e848187ef Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 11 Dec 2012 01:22:46 -0400 Subject: [PATCH] Add support for synchronizing bootstrap setups available with the official repository --- apps/bootstrap/links.py | 3 +- apps/bootstrap/literals.py | 3 ++ apps/bootstrap/managers.py | 39 ++++++++++++++++--- apps/bootstrap/permissions.py | 1 + apps/bootstrap/post_init.py | 7 ++-- apps/bootstrap/static/images/icons/world.png | Bin 0 -> 2568 bytes apps/bootstrap/urls.py | 1 + apps/bootstrap/views.py | 34 ++++++++++++++-- 8 files changed, 76 insertions(+), 12 deletions(-) create mode 100755 apps/bootstrap/static/images/icons/world.png diff --git a/apps/bootstrap/links.py b/apps/bootstrap/links.py index 4f15c4d24e..63e51dfa29 100644 --- a/apps/bootstrap/links.py +++ b/apps/bootstrap/links.py @@ -6,7 +6,7 @@ from .permissions import (PERMISSION_BOOTSTRAP_VIEW, PERMISSION_BOOTSTRAP_CREATE PERMISSION_BOOTSTRAP_EDIT, PERMISSION_BOOTSTRAP_DELETE, PERMISSION_BOOTSTRAP_EXECUTE, PERMISSION_BOOTSTRAP_DUMP, PERMISSION_NUKE_DATABASE, PERMISSION_BOOTSTRAP_EXPORT, - PERMISSION_BOOTSTRAP_IMPORT) + PERMISSION_BOOTSTRAP_IMPORT, PERMISSION_BOOTSTRAP_REPOSITORY_SYNC) link_bootstrap_setup_tool = {'text': _(u'bootstrap'), 'view': 'bootstrap_setup_list', 'icon': 'lightning.png', 'permissions': [PERMISSION_BOOTSTRAP_VIEW]} link_bootstrap_setup_list = {'text': _(u'bootstrap setup list'), 'view': 'bootstrap_setup_list', 'famfam': 'lightning', 'permissions': [PERMISSION_BOOTSTRAP_VIEW]} @@ -19,4 +19,5 @@ link_bootstrap_setup_dump = {'text': _(u'dump current setup'), 'view': 'bootstra link_bootstrap_setup_export = {'text': _(u'export'), 'view': 'bootstrap_setup_export', 'args': 'object.pk', 'famfam': 'disk', 'permissions': [PERMISSION_BOOTSTRAP_EXPORT]} link_bootstrap_setup_import_from_file = {'text': _(u'import from file'), 'view': 'bootstrap_setup_import_from_file', 'famfam': 'folder', 'permissions': [PERMISSION_BOOTSTRAP_IMPORT]} link_bootstrap_setup_import_from_url = {'text': _(u'import from URL'), 'view': 'bootstrap_setup_import_from_url', 'famfam': 'world', 'permissions': [PERMISSION_BOOTSTRAP_IMPORT]} +link_bootstrap_setup_repository_sync = {'text': _(u'sync with repository'), 'view': 'bootstrap_setup_repository_sync', 'famfam': 'world', 'permissions': [PERMISSION_BOOTSTRAP_REPOSITORY_SYNC]} link_erase_database = {'text': _(u'erase database'), 'view': 'erase_database_view', 'icon': 'radioactivity.png', 'permissions': [PERMISSION_NUKE_DATABASE]} diff --git a/apps/bootstrap/literals.py b/apps/bootstrap/literals.py index 250a84db63..c85577d1de 100644 --- a/apps/bootstrap/literals.py +++ b/apps/bootstrap/literals.py @@ -76,3 +76,6 @@ FIXTURE_METADATA_DESCRIPTION = 'description' BOOTSTRAP_EXTENSION = 'txt' BOOTSTRAP_SETUP_MAGIC_NUMBER = 'bootstrap setup' + +BOOTSTRAP_REPOSITORY_URL = 'http://bootstrap.mayan-edms.com' +BOOTSTRAP_REPOSITORY_INDEX_FILE = '_repo_index.txt' diff --git a/apps/bootstrap/managers.py b/apps/bootstrap/managers.py index ed94be0dd2..b5e0bea331 100644 --- a/apps/bootstrap/managers.py +++ b/apps/bootstrap/managers.py @@ -6,9 +6,13 @@ import requests from django.db import models from django.core import serializers +from django.utils.simplejson import loads +from django.db import IntegrityError +from django.db.models import Q from .classes import BootstrapModel, FixtureMetadata -from .literals import (FIXTURE_TYPE_FIXTURE_PROCESS, FIXTURE_TYPE_EMPTY_FIXTURE) +from .literals import (FIXTURE_TYPE_FIXTURE_PROCESS, FIXTURE_TYPE_EMPTY_FIXTURE, + BOOTSTRAP_REPOSITORY_URL, BOOTSTRAP_REPOSITORY_INDEX_FILE) logger = logging.getLogger(__name__) @@ -28,19 +32,44 @@ class BootstrapSetupManager(models.Manager): result.append(model_fixture) return FIXTURE_TYPE_FIXTURE_PROCESS[serialization_format]('\n'.join(result)) - def import_setup(self, file_data): + def import_setup(self, file_data, overwrite=False): BootstrapModel.check_magic_number(file_data) metadata = FixtureMetadata.read_all(file_data) instance = self.model(fixture=file_data, **metadata) - instance.save(update_metadata=False) + try: + instance.save(update_metadata=False) + except IntegrityError: + if not overwrite: + raise + else: + # Delete conflicting bootstrap setups + query = Q() + if 'slug' in metadata: + query = query | Q(slug=metadata['slug']) + + if 'name' in metadata: + query = query | Q(name=metadata['name']) + + self.model.objects.filter(query).delete() + self.import_setup(file_data) def import_from_file(self, files): file_data = files.read() self.import_setup(file_data) - def import_from_url(self, url): + def import_from_url(self, url, **kwargs): response = requests.get(url) if response.status_code == requests.codes.ok: - self.import_setup(response.text) + self.import_setup(response.text, **kwargs) else: response.raise_for_status() + + def repository_sync(self): + response = requests.get('%s/%s' % (BOOTSTRAP_REPOSITORY_URL, BOOTSTRAP_REPOSITORY_INDEX_FILE)) + if response.status_code == requests.codes.ok: + for entry in loads(response.text): + bootstrap_setup_url = '%s/%s' % (BOOTSTRAP_REPOSITORY_URL, entry['filename']) + self.import_from_url(bootstrap_setup_url, overwrite=True) + else: + response.raise_for_status() + diff --git a/apps/bootstrap/permissions.py b/apps/bootstrap/permissions.py index 6102374e12..71165fe954 100644 --- a/apps/bootstrap/permissions.py +++ b/apps/bootstrap/permissions.py @@ -14,4 +14,5 @@ PERMISSION_BOOTSTRAP_EXECUTE = Permission.objects.register(namespace, 'bootstrap PERMISSION_BOOTSTRAP_DUMP = Permission.objects.register(namespace, 'bootstrap_dump', _(u'Dump the current project\s setup into a bootstrap setup')) PERMISSION_BOOTSTRAP_EXPORT = Permission.objects.register(namespace, 'bootstrap_export', _(u'Export bootstrap setups as files')) PERMISSION_BOOTSTRAP_IMPORT = Permission.objects.register(namespace, 'bootstrap_import', _(u'Import new bootstrap setups')) +PERMISSION_BOOTSTRAP_REPOSITORY_SYNC = Permission.objects.register(namespace, 'bootstrap_repo_sync', _(u'Sync the local bootstrap setups with a published repository')) PERMISSION_NUKE_DATABASE = Permission.objects.register(namespace, 'nuke_database', _(u'Erase the entire database and document storage')) diff --git a/apps/bootstrap/post_init.py b/apps/bootstrap/post_init.py index 0e72128cc4..3b61c1f0f6 100644 --- a/apps/bootstrap/post_init.py +++ b/apps/bootstrap/post_init.py @@ -8,7 +8,8 @@ from main import __version__ from .links import (link_bootstrap_setup_create, link_bootstrap_setup_execute, link_bootstrap_setup_list, link_bootstrap_setup_edit, link_bootstrap_setup_delete, link_bootstrap_setup_view, link_bootstrap_setup_dump, link_bootstrap_setup_export, - link_bootstrap_setup_import_from_url, link_bootstrap_setup_import_from_file) + link_bootstrap_setup_import_from_url, link_bootstrap_setup_import_from_file, + link_bootstrap_setup_repository_sync) from .models import BootstrapSetup from .classes import FixtureMetadata from .literals import (FIXTURE_METADATA_CREATED, FIXTURE_METADATA_EDITED, @@ -16,8 +17,8 @@ from .literals import (FIXTURE_METADATA_CREATED, FIXTURE_METADATA_EDITED, FIXTURE_METADATA_DESCRIPTION, DATETIME_STRING_FORMAT, FIXTURE_METADATA_SLUG) register_links([BootstrapSetup], [link_bootstrap_setup_view, link_bootstrap_setup_edit, link_bootstrap_setup_delete, link_bootstrap_setup_execute, link_bootstrap_setup_export]) -register_links([BootstrapSetup], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump, link_bootstrap_setup_import_from_file, link_bootstrap_setup_import_from_url], menu_name='secondary_menu') -register_links(['bootstrap_setup_list', 'bootstrap_setup_create', 'bootstrap_setup_dump', 'bootstrap_setup_import_from_file', 'bootstrap_setup_import_from_url'], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump, link_bootstrap_setup_import_from_file, link_bootstrap_setup_import_from_url], menu_name='secondary_menu') +register_links([BootstrapSetup], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump, link_bootstrap_setup_import_from_file, link_bootstrap_setup_import_from_url, link_bootstrap_setup_repository_sync], menu_name='secondary_menu') +register_links(['bootstrap_setup_list', 'bootstrap_setup_create', 'bootstrap_setup_dump', 'bootstrap_setup_import_from_file', 'bootstrap_setup_import_from_url', 'bootstrap_setup_repository_sync'], [link_bootstrap_setup_list, link_bootstrap_setup_create, link_bootstrap_setup_dump, link_bootstrap_setup_import_from_file, link_bootstrap_setup_import_from_url, link_bootstrap_setup_repository_sync], menu_name='secondary_menu') FixtureMetadata(FIXTURE_METADATA_CREATED, generate_function=lambda fixture_instance: fixture_instance.created.strftime(DATETIME_STRING_FORMAT), read_function=lambda x: datetime.datetime.strptime(x, DATETIME_STRING_FORMAT), property_name='created') FixtureMetadata(FIXTURE_METADATA_EDITED, generate_function=lambda fixture_instance: datetime.datetime.now().strftime(DATETIME_STRING_FORMAT)) diff --git a/apps/bootstrap/static/images/icons/world.png b/apps/bootstrap/static/images/icons/world.png new file mode 100755 index 0000000000000000000000000000000000000000..644d9d53a7543f12e9893d0ff49bf8882e5df340 GIT binary patch literal 2568 zcmV+j3itJiP)B(6_~7)tc>NzGP!(mD z|Ai%6Xg&S^Az&|jzP-O;LRsmyMOA1l>x5gBpct1R8wp$ttVL20k=8YYlI3&+J{tz& z!XL3@>SM^Wm0+T0A?#*7ezJchz8($^k8JE40r zbuGywzu!sg8#jW9w}Kb79_(M=Jhgmf!$gdC*)SNM0}VrDe)to!pp(y3QzJ8>8Uhh; zLX`Rl^e}8f0Xi=q!uzMsKvnGsYRAzPeHLry?8T~Cx#i=YKitnS{+kD+lAr(i?fR;U z1#4zcMP05*yEKVCEkwYoDMQvpx>ih}cF3BDxF(^h_{S(Md=169d*F7Q!f1L50*UeX zXWuWOn+fE3jv=g_#O(4q3DPAE#reL#o!5?o#EbT#l_WNk4d>l)r-H+nzvxv%d zlpWQO$ZKzQj>o3&bOY|4?u7}w0_T0};B|IkaqTKl zdZ1;&i+D$F*hQG%WuoLB@XmX%$l>MrI08)w z(Bl2jj8VuG7L9-uBaNo&8hknQ0`jtkQIgXENz!OnDL8CV3gbA~Z3-@o`q5l_0D_}% zzIWc*qOlN2S?F&iP9D%rPJ*Rak{HNFfIvq{`B4teyb+a4;fppQt(3wbtu&0^ipfR0sN&e^ zw_vG^vPBdXIHWkb{c91H6*Nrfgy}APlwmUhMBh=D<;|s+#+08i)#ToYt1R^{Pz8(= zFe4Db!Lf zn~BiIosyB6J?rG&a}~W(9T&%CbRqHU9`l{(t$z86`c{q#i))X`IC7ft{7w{A~H~1@GJ+`OxA?- zu^z~Q?b0a!$-*!YT8rM`I$m`Rx@lxcnzT6O6?#MW)7uJxast->>}BR=-4M|mAp%MU zTs06QVlmQyAzmhMJ+m9(rgp-9G1p%3x8PL&D|9JAfLextW$d3QC^$X1k&dK(LA_pz zSxb$|Hq?~AhmoWWJwzfwabP4O)M=tbpl{@14svt%Vhx#z3|zd&F79S45x7DjGRQIn z40dEKK*7~mEn4>X!YQS0>;-JuARegS0*|u~LoopZ2?Cc1T-A`IrqVt149^)JFTNX- z|CS^xkmrY2)Ab~Q5;(;|C(vtl%?m7aGQF4J={ZKggp%s<`E69I7hvl6cR^m~;K4L? zIwGLyGQ#N!j8f}mfE<{tKe!a<2Dc!R8b?KFGXbfTrUn%$+)UsGW1essDHuw!hhej3 zXGa2 zqRu(j0;d9|WdJoZbIcgqoJUVU82oBa{M1JsjDVo|FZ&$Rzx|ZzzK^^x8!Q2d>va_y z24_AJuSc9b(Kul*oc3-i(+yZ!zl?yJk>%{i^7>UM&H04#eWR7i)IDXL1{u~@=v<{E zBT=G-FtYEZt$HLd%m|ozG$3TpTfIhdyYlTc{4p@gz)9%*XmA&YllS71zZq`3A2X|7 zLTPT81lov>o*g*S(@KI&qbTch)RcUHl;XiX1s&)QPsfD36PP^i?-&WxAuB#~CUo%4 z-Gm((6Lp?<){+GU4{d8JuBs)WNhC5>y#z~iLgq29Xo-rEo!so8nZSW5rGLWe#*KLQ z_#rHx-hx7}kB5!b1SWi|5?WdEi;sT?eRvW^+g@D}|E&E}Mqq3nNK!ny)T{k%qrL$? z?I=1k!!k{`jLubq3XG8?E>TL$Ainf1Lu*?M*{+Ky&Y|{9rCYwmsGQ*Ja1BC3GhqaJ z|4QIr@RYe>AYnNX^!@gU=agtDm<-dzOE0l@EVH(pF@DZT>70m0?N~Qi>ZQ?8v6t`K zs(QFqnpuiQ(k|>j`UZ0JtHGh)Y;2{=AuhBV2$&g0!vot_tw{SX2jobQ1Y~j&*F2#! z61qi5w>+Y0Oz|p3N#Ut!RXHn79n@)ju`>Ls?KqrKGjL_c;uQ=dgJs@sEN25F8x*Q0 zY;XHe%+4=BUKuSe@GXrs7Hfys>%6Mlc;(wLcLR#O-K2v6C-F(o68!DNb8yMY(3KsF zR?@ocBPDR-cz)|xZtzk?-j8(bYxmU7Eu)2MXtsf3bMT6$=~hEAIVM3NqR_du=IIUpWlRp!4iyL%T zHdM(4)#jp|vw6p`Qj~IDqdcEq`5wCZ7DG(gA;kyJMnBs5a