mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 11:26:56 -08:00
administer communities - list and edit
This commit is contained in:
parent
ede9c32953
commit
c1971b3d8d
17 changed files with 351 additions and 13 deletions
|
@ -21,7 +21,7 @@ class SiteMiscForm(FlaskForm):
|
|||
enable_downvotes = BooleanField(_l('Enable downvotes'))
|
||||
allow_local_image_posts = BooleanField(_l('Allow local image posts'))
|
||||
remote_image_cache_days = IntegerField(_l('Days to cache images from remote instances for'))
|
||||
enable_nsfw = BooleanField(_l('Allow NSFW communities and posts'))
|
||||
enable_nsfw = BooleanField(_l('Allow NSFW communities'))
|
||||
enable_nsfl = BooleanField(_l('Allow NSFL communities and posts'))
|
||||
community_creation_admin_only = BooleanField(_l('Only admins can create new local communities'))
|
||||
reports_email_admins = BooleanField(_l('Notify admins about reports, not just moderators'))
|
||||
|
@ -37,3 +37,43 @@ class FederationForm(FlaskForm):
|
|||
use_blocklist = BooleanField(_l('Blocklist instead of allowlist'))
|
||||
blocklist = TextAreaField(_l('Deny federation with these instances'))
|
||||
submit = SubmitField(_l('Save'))
|
||||
|
||||
|
||||
class EditCommunityForm(FlaskForm):
|
||||
title = StringField(_l('Title'), validators=[DataRequired()])
|
||||
url = StringField(_l('Url'), validators=[DataRequired()])
|
||||
description = TextAreaField(_l('Description'))
|
||||
icon_file = FileField(_('Icon image'))
|
||||
banner_file = FileField(_('Banner image'))
|
||||
rules = TextAreaField(_l('Rules'))
|
||||
nsfw = BooleanField('Porn community')
|
||||
show_home = BooleanField('Posts show on home page')
|
||||
show_popular = BooleanField('Posts can be popular')
|
||||
show_all = BooleanField('Posts show in All list')
|
||||
low_quality = BooleanField("Low quality / toxic - upvotes in here don't add to reputation")
|
||||
options = [(-1, _l('Forever')),
|
||||
(7, _l('1 week')),
|
||||
(14, _l('2 weeks')),
|
||||
(28, _l('1 month')),
|
||||
(56, _l('2 months')),
|
||||
(84, _l('3 months')),
|
||||
(168, _l('6 months')),
|
||||
(365, _l('1 year')),
|
||||
(730, _l('2 years')),
|
||||
(1825, _l('5 years')),
|
||||
(3650, _l('10 years')),
|
||||
]
|
||||
content_retention = SelectField(_l('Retain content'), choices=options, default=1, coerce=int)
|
||||
submit = SubmitField(_l('Save'))
|
||||
|
||||
def validate(self, extra_validators=None):
|
||||
if not super().validate():
|
||||
return False
|
||||
if self.url.data.strip() == '':
|
||||
self.url.errors.append(_('Url is required.'))
|
||||
return False
|
||||
else:
|
||||
if '-' in self.url.data.strip():
|
||||
self.url.errors.append(_('- cannot be in Url. Use _ instead?'))
|
||||
return False
|
||||
return True
|
|
@ -1,14 +1,15 @@
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
from flask import request, flash, json, url_for, current_app
|
||||
from flask import request, flash, json, url_for, current_app, redirect
|
||||
from flask_login import login_required, current_user
|
||||
from flask_babel import _
|
||||
from sqlalchemy import text, desc
|
||||
|
||||
from app import db
|
||||
from app.activitypub.routes import process_inbox_request, process_delete_request
|
||||
from app.admin.forms import FederationForm, SiteMiscForm, SiteProfileForm
|
||||
from app.models import AllowedInstances, BannedInstances, ActivityPubLog, utcnow, Site
|
||||
from app.admin.forms import FederationForm, SiteMiscForm, SiteProfileForm, EditCommunityForm
|
||||
from app.community.util import save_icon_file, save_banner_file
|
||||
from app.models import AllowedInstances, BannedInstances, ActivityPubLog, utcnow, Site, Community
|
||||
from app.utils import render_template, permission_required, set_setting, get_setting
|
||||
from app.admin import bp
|
||||
|
||||
|
@ -158,3 +159,76 @@ def activity_replay(activity_id):
|
|||
else:
|
||||
process_inbox_request(request_json, activity.id)
|
||||
return 'Ok'
|
||||
|
||||
|
||||
@bp.route('/communities', methods=['GET'])
|
||||
@login_required
|
||||
@permission_required('administer all communities')
|
||||
def admin_communities():
|
||||
|
||||
page = request.args.get('page', 1, type=int)
|
||||
|
||||
communities = Community.query.order_by(Community.title).paginate(page=page, per_page=1000, error_out=False)
|
||||
|
||||
next_url = url_for('admin.admin_communities', page=communities.next_num) if communities.has_next else None
|
||||
prev_url = url_for('admin.admin_communities', page=communities.prev_num) if communities.has_prev and page != 1 else None
|
||||
|
||||
return render_template('admin/communities.html', title=_('Communities'), next_url=next_url, prev_url=prev_url,
|
||||
communities=communities)
|
||||
|
||||
|
||||
@bp.route('/community/<int:community_id>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@permission_required('administer all communities')
|
||||
def admin_community_edit(community_id):
|
||||
form = EditCommunityForm()
|
||||
community = Community.query.get_or_404(community_id)
|
||||
if form.validate_on_submit():
|
||||
community.name = form.url.data
|
||||
community.title = form.title.data
|
||||
community.description = form.description.data
|
||||
community.rules = form.rules.data
|
||||
community.nsfw = form.nsfw.data
|
||||
community.show_home = form.show_home.data
|
||||
community.show_popular = form.show_popular.data
|
||||
community.show_all = form.show_all.data
|
||||
community.low_quality = form.low_quality.data
|
||||
community.content_retention = form.content_retention.data
|
||||
icon_file = request.files['icon_file']
|
||||
if icon_file and icon_file.filename != '':
|
||||
if community.icon_id:
|
||||
community.icon.delete_from_disk()
|
||||
file = save_icon_file(icon_file)
|
||||
if file:
|
||||
community.icon = file
|
||||
banner_file = request.files['banner_file']
|
||||
if banner_file and banner_file.filename != '':
|
||||
if community.image_id:
|
||||
community.image.delete_from_disk()
|
||||
file = save_banner_file(banner_file)
|
||||
if file:
|
||||
community.image = file
|
||||
db.session.commit()
|
||||
flash(_('Saved'))
|
||||
return redirect(url_for('admin.admin_communities'))
|
||||
else:
|
||||
if not community.is_local():
|
||||
flash(_('This is a remote community - most settings here will be regularly overwritten with data from the original server.'), 'warning')
|
||||
form.url.data = community.name
|
||||
form.title.data = community.title
|
||||
form.description.data = community.description
|
||||
form.rules.data = community.rules
|
||||
form.nsfw.data = community.nsfw
|
||||
form.show_home.data = community.show_home
|
||||
form.show_popular.data = community.show_popular
|
||||
form.show_all.data = community.show_all
|
||||
form.low_quality.data = community.low_quality
|
||||
form.content_retention.data = community.content_retention
|
||||
return render_template('admin/edit_community.html', title=_('Edit community'), form=form, community=community)
|
||||
|
||||
|
||||
@bp.route('/community/<int:community_id>/delete', methods=['GET'])
|
||||
@login_required
|
||||
@permission_required('administer all communities')
|
||||
def admin_community_delete(community_id):
|
||||
return ''
|
|
@ -3,14 +3,14 @@ from flask import redirect, url_for, flash, request, make_response, session, Mar
|
|||
from werkzeug.urls import url_parse
|
||||
from flask_login import login_user, logout_user, current_user
|
||||
from flask_babel import _
|
||||
from app import db
|
||||
from app import db, cache
|
||||
from app.auth import bp
|
||||
from app.auth.forms import LoginForm, RegistrationForm, ResetPasswordRequestForm, ResetPasswordForm
|
||||
from app.auth.util import random_token
|
||||
from app.models import User, utcnow, IpBan
|
||||
from app.auth.email import send_password_reset_email, send_welcome_email, send_verification_email
|
||||
from app.activitypub.signature import RsaKeys
|
||||
from app.utils import render_template, ip_address, user_ip_banned, user_cookie_banned
|
||||
from app.utils import render_template, ip_address, user_ip_banned, user_cookie_banned, banned_ip_addresses
|
||||
|
||||
|
||||
@bp.route('/login', methods=['GET', 'POST'])
|
||||
|
@ -50,6 +50,7 @@ def login():
|
|||
new_ip_ban = IpBan(ip_address=ip_address(), notes=user.user_name + ' used new IP address')
|
||||
db.session.add(new_ip_ban)
|
||||
db.session.commit()
|
||||
cache.delete_memoized('banned_ip_addresses')
|
||||
|
||||
# Set a cookie so we have another way to track banned people
|
||||
response.set_cookie('sesion', '17489047567495', expires=datetime(year=2099, month=12, day=30))
|
||||
|
|
|
@ -110,6 +110,7 @@ def register(app):
|
|||
admin_role.permissions.append(RolePermission(permission='ban users'))
|
||||
admin_role.permissions.append(RolePermission(permission='manage users'))
|
||||
admin_role.permissions.append(RolePermission(permission='change instance settings'))
|
||||
admin_role.permissions.append(RolePermission(permission='administer all communities'))
|
||||
db.session.add(admin_role)
|
||||
|
||||
# Admin user
|
||||
|
|
|
@ -13,7 +13,7 @@ class AddLocalCommunity(FlaskForm):
|
|||
icon_file = FileField(_('Icon image'))
|
||||
banner_file = FileField(_('Banner image'))
|
||||
rules = TextAreaField(_l('Rules'))
|
||||
nsfw = BooleanField('18+ NSFW')
|
||||
nsfw = BooleanField('NSFW')
|
||||
submit = SubmitField(_l('Create'))
|
||||
|
||||
def validate(self, extra_validators=None):
|
||||
|
@ -45,7 +45,7 @@ class CreatePostForm(FlaskForm):
|
|||
image_file = FileField(_('Image'))
|
||||
# flair = SelectField(_l('Flair'), coerce=int)
|
||||
nsfw = BooleanField(_l('NSFW'))
|
||||
nsfl = BooleanField(_l('NSFL'))
|
||||
nsfl = BooleanField(_l('Content warning'))
|
||||
notify_author = BooleanField(_l('Notify about replies'))
|
||||
submit = SubmitField(_l('Save'))
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ from datetime import timezone
|
|||
@login_required
|
||||
def add_local():
|
||||
form = AddLocalCommunity()
|
||||
if get_setting('allow_nsfw', False) is False:
|
||||
if g.site.enable_nsfw is False:
|
||||
form.nsfw.render_kw = {'disabled': True}
|
||||
|
||||
if form.validate_on_submit() and not community_url_exists(form.url.data):
|
||||
|
@ -289,9 +289,9 @@ def unsubscribe(actor):
|
|||
def add_post(actor):
|
||||
community = actor_to_community(actor)
|
||||
form = CreatePostForm()
|
||||
if get_setting('allow_nsfw', False) is False:
|
||||
if g.site.enable_nsfw is False:
|
||||
form.nsfw.render_kw = {'disabled': True}
|
||||
if get_setting('allow_nsfl', False) is False:
|
||||
if g.site.enable_nsfl is False:
|
||||
form.nsfl.render_kw = {'disabled': True}
|
||||
if community.nsfw:
|
||||
form.nsfw.data = True
|
||||
|
|
|
@ -89,6 +89,7 @@ class Community(db.Model):
|
|||
last_active = db.Column(db.DateTime, default=utcnow)
|
||||
public_key = db.Column(db.Text)
|
||||
private_key = db.Column(db.Text)
|
||||
content_retention = db.Column(db.Integer, default=-1)
|
||||
|
||||
ap_id = db.Column(db.String(255), index=True)
|
||||
ap_profile_id = db.Column(db.String(255), index=True)
|
||||
|
@ -108,6 +109,11 @@ class Community(db.Model):
|
|||
searchable = db.Column(db.Boolean, default=True)
|
||||
private_mods = db.Column(db.Boolean, default=False)
|
||||
|
||||
# Which feeds posts from this community show up in
|
||||
show_home = db.Column(db.Boolean, default=False) # For anonymous users. When logged in, the home feed shows posts from subscribed communities
|
||||
show_popular = db.Column(db.Boolean, default=True)
|
||||
show_all = db.Column(db.Boolean, default=True)
|
||||
|
||||
search_vector = db.Column(TSVectorType('name', 'title', 'description', 'rules'))
|
||||
|
||||
posts = db.relationship('Post', backref='community', lazy='dynamic', cascade="all, delete-orphan")
|
||||
|
@ -749,6 +755,7 @@ class Instance(db.Model):
|
|||
dormant = db.Column(db.Boolean, default=False) # True once this instance is considered offline and not worth sending to any more
|
||||
start_trying_again = db.Column(db.DateTime) # When to start trying again. Should grow exponentially with each failure.
|
||||
gone_forever = db.Column(db.Boolean, default=False) # True once this instance is considered offline forever - never start trying again
|
||||
ip_address = db.Column(db.String(50))
|
||||
|
||||
posts = db.relationship('Post', backref='instance', lazy='dynamic')
|
||||
post_replies = db.relationship('PostReply', backref='instance', lazy='dynamic')
|
||||
|
|
|
@ -22,3 +22,19 @@
|
|||
.pl-0 {
|
||||
padding-left: 0!important;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 5px!important;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 10px!important;
|
||||
}
|
||||
|
||||
.pl-3 {
|
||||
padding-left: 15px!important;
|
||||
}
|
||||
|
||||
.pl-4 {
|
||||
padding-left: 20px!important;
|
||||
}
|
|
@ -12,6 +12,22 @@ nav, etc which are used site-wide */
|
|||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 5px !important;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
.pl-3 {
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
|
||||
.pl-4 {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
/* for more info about the feather font used for icons see https://at-ui.github.io/feather-font/ */
|
||||
/* use https://fontdrop.info/ to get the unicode values of the featuer.ttf file */
|
||||
@font-face {
|
||||
|
@ -684,4 +700,8 @@ fieldset legend {
|
|||
padding-right: 5px;
|
||||
}
|
||||
|
||||
fieldset legend {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=structure.css.map */
|
||||
|
|
|
@ -407,3 +407,9 @@ nav, etc which are used site-wide */
|
|||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
legend {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,22 @@
|
|||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 5px !important;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
.pl-3 {
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
|
||||
.pl-4 {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
/* for more info about the feather font used for icons see https://at-ui.github.io/feather-font/ */
|
||||
/* use https://fontdrop.info/ to get the unicode values of the featuer.ttf file */
|
||||
@font-face {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<a href="{{ url_for('admin.admin_home') }}">{{ _('Home') }}</a> |
|
||||
<a href="{{ url_for('admin.admin_site') }}">{{ _('Site profile') }}</a> |
|
||||
<a href="{{ url_for('admin.admin_misc') }}">{{ _('Misc settings') }}</a> |
|
||||
<a href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a> |
|
||||
<a href="{{ url_for('admin.admin_federation') }}">{{ _('Federation') }}</a> |
|
||||
<a href="{{ url_for('admin.admin_activities') }}">{{ _('Activities') }}</a>
|
||||
</nav>
|
||||
|
|
58
app/templates/admin/communities.html
Normal file
58
app/templates/admin/communities.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
{% extends "base.html" %}
|
||||
{% from 'bootstrap/form.html' import render_form %}
|
||||
|
||||
{% block app_content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% include 'admin/_nav.html' %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<form method="get">
|
||||
<input type="search" name="search"> <input type="submit" name="submit" value="Search">
|
||||
</form>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Title</th>
|
||||
<th># Posts</th>
|
||||
<th>Home</th>
|
||||
<th>Popular</th>
|
||||
<th>All</th>
|
||||
<th>Quality</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
{% for community in communities %}
|
||||
<tr>
|
||||
<td>{{ community.name }}</td>
|
||||
<td><img src="{{ community.icon_image('tiny') }}" class="community_icon rounded-circle" loading="lazy" />
|
||||
{{ community.display_name() }}</td>
|
||||
<td>{{ community.post_count }}</td>
|
||||
<th>{{ '✓'|safe if community.show_home else '✗'|safe }}</th>
|
||||
<th>{{ '✓'|safe if community.show_popular else '✗'|safe }}</th>
|
||||
<th>{{ '✓'|safe if community.show_all else '✗'|safe }}</th>
|
||||
<th>{{ '<span class="fe fe-arrow-down"> </span>'|safe if community.low_quality else ' ' }}</th>
|
||||
<td><a href="/c/{{ community.link() }}">View</a> |
|
||||
<a href="{{ url_for('admin.admin_community_edit', community_id=community.id) }}">Edit</a> |
|
||||
<a href="{{ url_for('admin.admin_community_delete', community_id=community.id) }}" class="confirm_first">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<nav aria-label="Pagination" class="mt-4">
|
||||
{% if prev_url %}
|
||||
<a href="{{ prev_url }}" class="btn btn-primary">
|
||||
<span aria-hidden="true">←</span> {{ _('Previous page') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if next_url %}
|
||||
<a href="{{ next_url }}" class="btn btn-primary">
|
||||
{{ _('Next page') }} <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
52
app/templates/admin/edit_community.html
Normal file
52
app/templates/admin/edit_community.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends "base.html" %}
|
||||
{% from 'bootstrap/form.html' import render_field %}
|
||||
|
||||
{% block app_content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% include 'admin/_nav.html' %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col col-login mx-auto">
|
||||
<h3>{{ _('Edit %(community_name)s', community_name=community.display_name()) }}</h3>
|
||||
<form method="post" enctype="multipart/form-data" id="add_local_community_form">
|
||||
{{ form.csrf_token() }}
|
||||
{{ render_field(form.title) }}
|
||||
<div class="form-group">{{ form.url.label(class_="form-control-label required") }}
|
||||
/c/{{ form.url(class_="form-control", maxlength=255) }}
|
||||
{% for error in form.url.errors %}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ render_field(form.description) }}
|
||||
{% if community.icon_id %}
|
||||
<img class="community_icon_big rounded-circle" src="{{ community.icon_image() }}" />
|
||||
{% endif %}
|
||||
{{ render_field(form.icon_file) }}
|
||||
<small class="field_hint">Provide a square image that looks good when small.</small>
|
||||
{% if community.image_id %}
|
||||
<a href="{{ community.header_image() }}"><img class="community_icon_big" src="{{ community.header_image() }}" /></a>
|
||||
{% endif %}
|
||||
{{ render_field(form.banner_file) }}
|
||||
<small class="field_hint">Provide a wide image - letterbox orientation.</small>
|
||||
{{ render_field(form.rules) }}
|
||||
{{ render_field(form.nsfw) }}
|
||||
{% if not community.is_local() %}
|
||||
<fieldset class="border pl-2 pt-2 mb-4">
|
||||
<legend>{{ _('Will not be overwritten by remote server') }}</legend>
|
||||
{% endif %}
|
||||
{{ render_field(form.show_home) }}
|
||||
{{ render_field(form.show_popular) }}
|
||||
{{ render_field(form.show_all) }}
|
||||
{{ render_field(form.low_quality) }}
|
||||
{{ render_field(form.content_retention) }}
|
||||
{% if not community.is_local() %}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
{{ render_field(form.submit) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -44,7 +44,9 @@
|
|||
<ul class="list-group list-group-flush">
|
||||
{% for community in active_communities %}
|
||||
<li class="list-group-item">
|
||||
<a href="/c/{{ community.link() }}"><img src="{{ community.icon_image() }}" class="community_icon rounded-circle" loading="lazy" />{{ community.display_name() }}</a>
|
||||
<a href="/c/{{ community.link() }}"><img src="{{ community.icon_image() }}" class="community_icon rounded-circle" loading="lazy" />
|
||||
{{ community.display_name() }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -365,7 +365,7 @@ def user_cookie_banned() -> bool:
|
|||
return cookie is not None
|
||||
|
||||
|
||||
@cache.cached(timeout=300)
|
||||
@cache.memoize(timeout=300)
|
||||
def banned_ip_addresses() -> List[str]:
|
||||
ips = IpBan.query.all()
|
||||
return [ip.ip_address for ip in ips]
|
||||
|
|
44
migrations/versions/c80716fd7b79_feeds.py
Normal file
44
migrations/versions/c80716fd7b79_feeds.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
"""feeds
|
||||
|
||||
Revision ID: c80716fd7b79
|
||||
Revises: 3f17b9ab55e4
|
||||
Create Date: 2023-12-31 12:05:39.109343
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c80716fd7b79'
|
||||
down_revision = '3f17b9ab55e4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('community', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('content_retention', sa.Integer(), nullable=True))
|
||||
batch_op.add_column(sa.Column('show_home', sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column('show_popular', sa.Boolean(), nullable=True))
|
||||
batch_op.add_column(sa.Column('show_all', sa.Boolean(), nullable=True))
|
||||
|
||||
with op.batch_alter_table('instance', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('ip_address', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('instance', schema=None) as batch_op:
|
||||
batch_op.drop_column('ip_address')
|
||||
|
||||
with op.batch_alter_table('community', schema=None) as batch_op:
|
||||
batch_op.drop_column('show_all')
|
||||
batch_op.drop_column('show_popular')
|
||||
batch_op.drop_column('show_home')
|
||||
batch_op.drop_column('content_retention')
|
||||
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in a new issue