Continue config the backup moduel

This commit is contained in:
Roberto Rosario
2012-08-18 10:22:46 -04:00
parent 08dc52c653
commit 00b9bf6061
4810 changed files with 250 additions and 40 deletions

View File

@@ -11,27 +11,14 @@ from project_tools.api import register_tool
from project_setup.api import register_setup
from navigation.api import bind_links, register_model_list_columns
from .api import register_app
from .classes import AppBackup, ModelBackup
from .exceptions import UnableToRegister
from .links import (app_registry_tool_link, app_list, backup_tool_link,
restore_tool_link, backup_job_list, backup_job_create, backup_job_edit,
backup_job_test)
from .literals import BACKUP_JOB_QUEUE_NAME
from .models import App, BackupJob
register_tool(app_registry_tool_link)
bind_links(['app_list'], [app_list], menu_name='secondary_menu')
try:
app = register_app('app_registry', label=_(u'App registry'), icon=APP)
except UnableToRegister:
pass
#else:
# AppBackup(app, [ModelBackup()])
# TODO: move to literals
BACKUP_JOB_QUEUE_NAME = 'backups_queue'
@transaction.commit_on_success
def create_backups_job_queue():
@@ -42,6 +29,9 @@ def create_backups_job_queue():
transaction.rollback()
register_tool(app_registry_tool_link)
bind_links(['app_list'], [app_list], menu_name='secondary_menu')
create_backups_job_queue()
#backup_job_type = JobType('remote_backup', _(u'Remove backup'), do_backup)
@@ -56,3 +46,10 @@ register_model_list_columns(BackupJob, [
{'name':_(u'storage module'), 'attribute': 'storage_module.label'},
{'name':_(u'apps'), 'attribute': encapsulate(lambda x: u', '.join([unicode(app) for app in x.apps.all()]))},
])
try:
app = App.register('app_registry', label=_(u'App registry'), icon=APP, description=_(u'Holds the app registry and backups functions.'))
except App.UnableToRegister:
pass
else:
app.set_backup([ModelBackup()])

View File

@@ -1,22 +0,0 @@
from __future__ import absolute_import
from django.db import DatabaseError, transaction
from .models import App
from .links import app_registry_tool_link
from .exceptions import UnableToRegister
@transaction.commit_on_success
def register_app(name, label, icon=None):
try:
app, created = App.objects.get_or_create(name=name)
except DatabaseError:
transaction.rollback()
raise UnableToRegister
else:
app.label = label
if icon:
app.icon = icon
app.save()
return app

View File

@@ -1,2 +0,0 @@
class UnableToRegister(Exception):
pass

View File

@@ -0,0 +1 @@
BACKUP_JOB_QUEUE_NAME = 'backups_queue'

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'BackupJob'
db.create_table('app_registry_backupjob', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=64)),
('enabled', self.gf('django.db.models.fields.BooleanField')(default=True)),
('begin_datetime', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2012, 8, 18, 0, 0))),
('storage_module_name', self.gf('django.db.models.fields.CharField')(max_length=32)),
('storage_arguments_json', self.gf('django.db.models.fields.TextField')(blank=True)),
))
db.send_create_signal('app_registry', ['BackupJob'])
# Adding M2M table for field apps on 'BackupJob'
db.create_table('app_registry_backupjob_apps', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('backupjob', models.ForeignKey(orm['app_registry.backupjob'], null=False)),
('app', models.ForeignKey(orm['app_registry.app'], null=False))
))
db.create_unique('app_registry_backupjob_apps', ['backupjob_id', 'app_id'])
def backwards(self, orm):
# Deleting model 'BackupJob'
db.delete_table('app_registry_backupjob')
# Removing M2M table for field apps on 'BackupJob'
db.delete_table('app_registry_backupjob_apps')
models = {
'app_registry.app': {
'Meta': {'ordering': "('name',)", 'object_name': 'App'},
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'app_registry.backupjob': {
'Meta': {'object_name': 'BackupJob'},
'apps': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['app_registry.App']", 'symmetrical': 'False'}),
'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 18, 0, 0)'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'storage_module_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
}
}
complete_apps = ['app_registry']

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding M2M table for field dependencies on 'App'
db.create_table('app_registry_app_dependencies', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('from_app', models.ForeignKey(orm['app_registry.app'], null=False)),
('to_app', models.ForeignKey(orm['app_registry.app'], null=False))
))
db.create_unique('app_registry_app_dependencies', ['from_app_id', 'to_app_id'])
def backwards(self, orm):
# Removing M2M table for field dependencies on 'App'
db.delete_table('app_registry_app_dependencies')
models = {
'app_registry.app': {
'Meta': {'ordering': "('name',)", 'object_name': 'App'},
'dependencies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'dependencies_rel_+'", 'null': 'True', 'to': "orm['app_registry.App']"}),
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'app_registry.backupjob': {
'Meta': {'object_name': 'BackupJob'},
'apps': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['app_registry.App']", 'symmetrical': 'False'}),
'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 18, 0, 0)'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'storage_module_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
}
}
complete_apps = ['app_registry']

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Removing M2M table for field dependencies on 'App'
db.delete_table('app_registry_app_dependencies')
def backwards(self, orm):
# Adding M2M table for field dependencies on 'App'
db.create_table('app_registry_app_dependencies', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('from_app', models.ForeignKey(orm['app_registry.app'], null=False)),
('to_app', models.ForeignKey(orm['app_registry.app'], null=False))
))
db.create_unique('app_registry_app_dependencies', ['from_app_id', 'to_app_id'])
models = {
'app_registry.app': {
'Meta': {'ordering': "('name',)", 'object_name': 'App'},
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'app_registry.backupjob': {
'Meta': {'object_name': 'BackupJob'},
'apps': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['app_registry.App']", 'symmetrical': 'False'}),
'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 18, 0, 0)'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'storage_module_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
}
}
complete_apps = ['app_registry']

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding M2M table for field dependencies on 'App'
db.create_table('app_registry_app_dependencies', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('from_app', models.ForeignKey(orm['app_registry.app'], null=False)),
('to_app', models.ForeignKey(orm['app_registry.app'], null=False))
))
db.create_unique('app_registry_app_dependencies', ['from_app_id', 'to_app_id'])
def backwards(self, orm):
# Removing M2M table for field dependencies on 'App'
db.delete_table('app_registry_app_dependencies')
models = {
'app_registry.app': {
'Meta': {'ordering': "('name',)", 'object_name': 'App'},
'dependencies': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['app_registry.App']", 'null': 'True', 'blank': 'True'}),
'icon': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
},
'app_registry.backupjob': {
'Meta': {'object_name': 'BackupJob'},
'apps': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['app_registry.App']", 'symmetrical': 'False'}),
'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 18, 0, 0)'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'storage_module_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
}
}
complete_apps = ['app_registry']

View File

@@ -4,6 +4,7 @@ import datetime
import logging
from django.db import models
from django.db import DatabaseError, transaction
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.contrib.contenttypes.models import ContentType
@@ -17,8 +18,46 @@ logger = logging.getLogger(__name__)
class App(TranslatableLabelMixin, LiveObjectMixin, models.Model):
translatables = ['label', 'description']
class UnableToRegister(Exception):
pass
name = models.CharField(max_length=64, verbose_name=_(u'name'), unique=True)
icon = models.CharField(max_length=64, verbose_name=_(u'icon'), blank=True)
dependencies = models.ManyToManyField('self', verbose_name=_(u'dependencies'), symmetrical=False, blank=True, null=True)
#version
#top_urls
#namespace
@classmethod
@transaction.commit_on_success
def register(cls, name, label, icon=None, description=None):
try:
app, created = App.objects.get_or_create(name=name)
except DatabaseError:
transaction.rollback()
raise UnableToRegister
else:
app.label = label
if icon:
app.icon = icon
if description:
app.description = description
app.dependencies.clear()
app.save()
return app
def set_dependencies(self, app_names):
for app_name in app_names:
app = App.objects.get(name=app_name)
self.dependencies.add(app)
def set_backup(self, *args, **kwargs):
return AppBackup(self, *args, **kwargs)
def __unicode__(self):
return unicode(self.label)
class Meta:
ordering = ('name', )

View File

@@ -8,6 +8,9 @@ from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils.translation import ugettext as _
from common.utils import encapsulate
from icons.widgets import icon_widget
from icons.literals import APP
from permissions.models import Permission
from .classes import AppBackup
@@ -23,9 +26,11 @@ def app_list(request):
'object_list' : App.live.all(),
'hide_object': True,
'extra_columns': [
{'name': _(u'name'), 'attribute': 'name'},
{'name': _(u'label'), 'attribute': 'label'},
{'name': _(u'icon'), 'attribute': 'icon'},
{'name':_(u'icon'), 'attribute': encapsulate(lambda x: icon_widget(x.icon or APP))},
{'name': _(u'label'), 'attribute': 'label'},
{'name':_(u'description'), 'attribute': 'description'},
{'name':_(u'dependencies'), 'attribute': encapsulate(lambda x: u', '.join([unicode(dependency) for dependency in x.dependencies.all()]))},
],
}, context_instance=RequestContext(request))

View File

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 825 B

View File

Before

Width:  |  Height:  |  Size: 445 B

After

Width:  |  Height:  |  Size: 445 B

View File

Before

Width:  |  Height:  |  Size: 838 B

After

Width:  |  Height:  |  Size: 838 B

View File

Before

Width:  |  Height:  |  Size: 686 B

After

Width:  |  Height:  |  Size: 686 B

View File

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 712 B

View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

View File

Before

Width:  |  Height:  |  Size: 885 B

After

Width:  |  Height:  |  Size: 885 B

View File

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 674 B

View File

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 670 B

View File

Before

Width:  |  Height:  |  Size: 698 B

After

Width:  |  Height:  |  Size: 698 B

View File

Before

Width:  |  Height:  |  Size: 685 B

After

Width:  |  Height:  |  Size: 685 B

View File

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

View File

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

View File

Before

Width:  |  Height:  |  Size: 722 B

After

Width:  |  Height:  |  Size: 722 B

View File

Before

Width:  |  Height:  |  Size: 775 B

After

Width:  |  Height:  |  Size: 775 B

View File

Before

Width:  |  Height:  |  Size: 737 B

After

Width:  |  Height:  |  Size: 737 B

View File

Before

Width:  |  Height:  |  Size: 437 B

After

Width:  |  Height:  |  Size: 437 B

View File

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 433 B

View File

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 531 B

View File

Before

Width:  |  Height:  |  Size: 481 B

After

Width:  |  Height:  |  Size: 481 B

View File

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 532 B

View File

Before

Width:  |  Height:  |  Size: 452 B

After

Width:  |  Height:  |  Size: 452 B

View File

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 563 B

View File

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 532 B

View File

Before

Width:  |  Height:  |  Size: 809 B

After

Width:  |  Height:  |  Size: 809 B

View File

Before

Width:  |  Height:  |  Size: 605 B

After

Width:  |  Height:  |  Size: 605 B

View File

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 525 B

View File

Before

Width:  |  Height:  |  Size: 831 B

After

Width:  |  Height:  |  Size: 831 B

View File

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 583 B

View File

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 714 B

View File

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 777 B

View File

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 749 B

View File

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 716 B

View File

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View File

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 636 B

View File

Before

Width:  |  Height:  |  Size: 661 B

After

Width:  |  Height:  |  Size: 661 B

View File

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 584 B

After

Width:  |  Height:  |  Size: 584 B

View File

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 535 B

View File

Before

Width:  |  Height:  |  Size: 249 B

After

Width:  |  Height:  |  Size: 249 B

View File

Before

Width:  |  Height:  |  Size: 580 B

After

Width:  |  Height:  |  Size: 580 B

View File

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 534 B

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

Before

Width:  |  Height:  |  Size: 657 B

After

Width:  |  Height:  |  Size: 657 B

View File

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 525 B

View File

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 688 B

View File

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

View File

Before

Width:  |  Height:  |  Size: 759 B

After

Width:  |  Height:  |  Size: 759 B

View File

Before

Width:  |  Height:  |  Size: 713 B

After

Width:  |  Height:  |  Size: 713 B

View File

Before

Width:  |  Height:  |  Size: 694 B

After

Width:  |  Height:  |  Size: 694 B

View File

Before

Width:  |  Height:  |  Size: 641 B

After

Width:  |  Height:  |  Size: 641 B

View File

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 664 B

View File

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 623 B

View File

Before

Width:  |  Height:  |  Size: 663 B

After

Width:  |  Height:  |  Size: 663 B

View File

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

View File

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 599 B

View File

Before

Width:  |  Height:  |  Size: 468 B

After

Width:  |  Height:  |  Size: 468 B

View File

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 575 B

View File

Before

Width:  |  Height:  |  Size: 650 B

After

Width:  |  Height:  |  Size: 650 B

View File

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 483 B

View File

Before

Width:  |  Height:  |  Size: 649 B

After

Width:  |  Height:  |  Size: 649 B

View File

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 643 B

View File

Before

Width:  |  Height:  |  Size: 538 B

After

Width:  |  Height:  |  Size: 538 B

View File

Before

Width:  |  Height:  |  Size: 522 B

After

Width:  |  Height:  |  Size: 522 B

View File

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 394 B

View File

Before

Width:  |  Height:  |  Size: 422 B

After

Width:  |  Height:  |  Size: 422 B

View File

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 369 B

View File

Before

Width:  |  Height:  |  Size: 564 B

After

Width:  |  Height:  |  Size: 564 B

View File

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 587 B

View File

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 628 B

View File

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 588 B

View File

Before

Width:  |  Height:  |  Size: 554 B

After

Width:  |  Height:  |  Size: 554 B

View File

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

View File

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 494 B

View File

Before

Width:  |  Height:  |  Size: 538 B

After

Width:  |  Height:  |  Size: 538 B

View File

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

View File

Before

Width:  |  Height:  |  Size: 761 B

After

Width:  |  Height:  |  Size: 761 B

View File

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

View File

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 651 B

View File

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 381 B

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View File

Before

Width:  |  Height:  |  Size: 570 B

After

Width:  |  Height:  |  Size: 570 B

View File

Before

Width:  |  Height:  |  Size: 556 B

After

Width:  |  Height:  |  Size: 556 B

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 508 B

View File

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 632 B

View File

Before

Width:  |  Height:  |  Size: 661 B

After

Width:  |  Height:  |  Size: 661 B

View File

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 674 B

View File

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 558 B

View File

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 363 B

Some files were not shown because too many files have changed in this diff Show More