mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-01 05:49:58 +00:00
4749eccfa5
Fix for python2 attributeError instead of TypeError on login with wrong openLDAP setting Added default empty string on LDAPCertificate Fix ldap as scheme for tls connection Enabled add user on LDAP Authentication LDAP config port is now number input Added header for user import config Added python ldap version to about section Fix: It's no longer possible to login via fallback password as long as LDAP server is available Fix: TypeError on bind is now catched and transformed to error message Update Readme Fixes for ldap
376 lines
23 KiB
HTML
376 lines
23 KiB
HTML
{% extends "layout.html" %}
|
|
{% block body %}
|
|
<div class="discover">
|
|
<h2>{{title}}</h2>
|
|
<form role="form" method="POST" autocomplete="off">
|
|
<div class="panel-group">
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">
|
|
<a class="accordion-toggle" data-toggle="collapse" href="#collapseOne">
|
|
<span class="glyphicon glyphicon-minus"></span>
|
|
{{_('Library Configuration')}}
|
|
</a>
|
|
</h4>
|
|
</div>
|
|
<div id="collapseOne" class="panel-collapse collapse in">
|
|
<div class="panel-body">
|
|
<div class="form-group required">
|
|
<label for="config_calibre_dir">{{_('Location of Calibre Database')}}</label>
|
|
<input type="text" class="form-control" name="config_calibre_dir" id="config_calibre_dir" value="{% if config.config_calibre_dir != None %}{{ config.config_calibre_dir }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
{% if feature_support['gdrive'] %}
|
|
<div class="form-group required">
|
|
<input type="checkbox" id="config_use_google_drive" name="config_use_google_drive" data-control="gdrive_settings" {% if config.config_use_google_drive %}checked{% endif %} >
|
|
<label for="config_use_google_drive">{{_('Use Google Drive?')}}</label>
|
|
</div>
|
|
<div data-related="gdrive_settings">
|
|
{% if gdriveError %}
|
|
<div class="form-group">
|
|
<label>
|
|
{{_('Google Drive config problem')}}: {{ gdriveError }}
|
|
</label>
|
|
</div>
|
|
{% else %}
|
|
{% if show_authenticate_google_drive and g.user.is_authenticated and config.config_use_google_drive %}
|
|
<div class="form-group required">
|
|
<a href="{{ url_for('gdrive.authenticate_google_drive') }}" class="btn btn-primary">{{_('Authenticate Google Drive')}}</a>
|
|
</div>
|
|
{% else %}
|
|
{% if show_authenticate_google_drive and g.user.is_authenticated and not config.config_use_google_drive %}
|
|
<div >{{_('Please hit submit to continue with setup')}}</div>
|
|
{% endif %}
|
|
{% if not g.user.is_authenticated %}
|
|
<div >{{_('Please finish Google Drive setup after login')}}</div>
|
|
{% endif %}
|
|
{% if g.user.is_authenticated %}
|
|
{% if not show_authenticate_google_drive %}
|
|
<div class="form-group required">
|
|
<label for="config_google_drive_folder">{{_('Google Drive Calibre folder')}}</label>
|
|
<select name="config_google_drive_folder" id="config_google_drive_folder" class="form-control">
|
|
{% for gdrivefolder in gdrivefolders %}
|
|
<option value="{{ gdrivefolder.title }}" {% if gdrivefolder.title == config.config_google_drive_folder %}selected{% endif %}>{{ gdrivefolder.title }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% if config.config_google_drive_watch_changes_response %}
|
|
<label for="config_google_drive_watch_changes_response">{{_('Metadata Watch Channel ID')}}</label>
|
|
<div class="form-group input-group required">
|
|
<input type="text" class="form-control" name="config_google_drive_watch_changes_response" id="config_google_drive_watch_changes_response" value="{{ config.config_google_drive_watch_changes_response['id'] }} expires on {{ config.config_google_drive_watch_changes_response['expiration'] | strftime }}" autocomplete="off" disabled="">
|
|
<span class="input-group-btn"><a href="{{ url_for('gdrive.revoke_watch_gdrive') }}" class="btn btn-primary">{{_('Revoke')}}</a></span>
|
|
</div>
|
|
{% else %}
|
|
<a href="{{ url_for('gdrive.watch_gdrive') }}" class="btn btn-primary">Enable watch of metadata.db</a>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if show_back_button %}
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">
|
|
<a class="accordion-toggle" data-toggle="collapse" href="#collapsetwo">
|
|
<span class="glyphicon glyphicon-plus"></span>
|
|
{{_('Server Configuration')}}
|
|
</a>
|
|
</h4>
|
|
</div>
|
|
<div id="collapsetwo" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<div class="form-group">
|
|
<label for="config_port">{{_('Server Port')}}</label>
|
|
<input type="number" min="1" max="65535" class="form-control" name="config_port" id="config_port" value="{% if config.config_port != None %}{{ config.config_port }}{% endif %}" autocomplete="off" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_certfile">{{_('SSL certfile location (leave it empty for non-SSL Servers)')}}</label>
|
|
<input type="text" class="form-control" name="config_certfile" id="config_certfile" value="{% if config.config_certfile != None %}{{ config.config_certfile }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_keyfile">{{_('SSL Keyfile location (leave it empty for non-SSL Servers)')}}</label>
|
|
<input type="text" class="form-control" name="config_keyfile" id="config_keyfile" value="{% if config.config_keyfile != None %}{{ config.config_keyfile }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_updatechannel">{{_('Update Channel')}}</label>
|
|
<select name="config_updatechannel" id="config_updatechannel" class="form-control">
|
|
<option value="0" {% if config.config_updatechannel == 0 %}selected{% endif %}>{{_('Stable')}}</option>
|
|
<!--option value="1" {% if config.config_updatechannel == 1 %}selected{% endif %}>{{_('Stable (Automatic)')}}</option-->
|
|
<option value="2" {% if config.config_updatechannel == 2 %}selected{% endif %}>{{_('Nightly')}}</option>
|
|
<!--option-- value="3" {% if config.config_updatechannel == 3 %}selected{% endif %}>{{_('Nightly (Automatic)')}}</option-->
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">
|
|
<a class="accordion-toggle" data-toggle="collapse" href="#collapsethree">
|
|
<span class="glyphicon glyphicon-plus"></span>
|
|
{{_('Logfile Configuration')}}
|
|
</a>
|
|
</h4>
|
|
</div>
|
|
<div id="collapsethree" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<div class="form-group">
|
|
<label for="config_log_level">{{_('Log Level')}}</label>
|
|
<select name="config_log_level" id="config_log_level" class="form-control">
|
|
<option value="10" {% if config.config_log_level == 10 %}selected{% endif %}>DEBUG</option>
|
|
<option value="20" {% if config.config_log_level == 20 or config.config_log_level == None %}selected{% endif %}>INFO</option>
|
|
<option value="30" {% if config.config_log_level == 30 %}selected{% endif %}>WARNING</option>
|
|
<option value="40" {% if config.config_log_level == 40 %}selected{% endif %}>ERROR</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_logfile">{{_('Location and name of logfile (calibre-web.log for no entry)')}}</label>
|
|
<input type="text" class="form-control" name="config_logfile" id="config_logfile" value="{% if config.config_logfile != None %}{{ config.config_logfile }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_access_log" name="config_access_log" {% if config.config_access_log %}checked{% endif %}>
|
|
<label for="config_access_log">{{_('Enable Access Log')}}</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_access_logfile">{{_('Location and name of access logfile (access.log for no entry)')}}</label>
|
|
<input type="text" class="form-control" name="config_access_logfile" id="config_access_logfile" value="{% if config.config_access_logfile != None %}{{ config.config_access_logfile }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">
|
|
<a class="accordion-toggle" data-toggle="collapse" href="#collapsefive">
|
|
<span class="glyphicon glyphicon-plus"></span>
|
|
{{_('Feature Configuration')}}
|
|
</a>
|
|
</h4>
|
|
</div>
|
|
<div id="collapsefive" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_uploading" name="config_uploading" {% if config.config_uploading %}checked{% endif %}>
|
|
<label for="config_uploading">{{_('Enable Uploads')}}</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_anonbrowse" name="config_anonbrowse" {% if config.config_anonbrowse %}checked{% endif %}>
|
|
<label for="config_anonbrowse">{{_('Enable Anonymous Browsing')}}</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_public_reg" name="config_public_reg" {% if config.config_public_reg %}checked{% endif %}>
|
|
<label for="config_public_reg">{{_('Enable Public Registration')}}</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_remote_login" name="config_remote_login" {% if config.config_remote_login %}checked{% endif %}>
|
|
<label for="config_remote_login">{{_('Enable Magic Link Remote Login')}}</label>
|
|
</div>
|
|
{% if feature_support['kobo'] %}
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_kobo_sync" name="config_kobo_sync" data-control="kobo-settings" {% if config.config_kobo_sync %}checked{% endif %}>
|
|
<label for="config_kobo_sync">{{_('Enable Kobo sync')}}</label>
|
|
</div>
|
|
<div data-related="kobo-settings">
|
|
<div class="form-group" style="text-indent:10px;">
|
|
<input type="checkbox" id="config_kobo_proxy" name="config_kobo_proxy" {% if config.config_kobo_proxy %}checked{% endif %}>
|
|
<label for="config_kobo_proxy">{{_('Proxy unknown requests to Kobo Store')}}</label>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if feature_support['goodreads'] %}
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_use_goodreads" name="config_use_goodreads" data-control="goodreads-settings" {% if config.config_use_goodreads %}checked{% endif %}>
|
|
<label for="config_use_goodreads">{{_('Use Goodreads')}}</label>
|
|
<a href="https://www.goodreads.com/api/keys" target="_blank" style="margin-left: 5px">{{_('Create an API Key')}}</a>
|
|
</div>
|
|
<div data-related="goodreads-settings">
|
|
<div class="form-group">
|
|
<label for="config_goodreads_api_key">{{_('Goodreads API Key')}}</label>
|
|
<input type="text" class="form-control" id="config_goodreads_api_key" name="config_goodreads_api_key" value="{% if config.config_goodreads_api_key != None %}{{ config.config_goodreads_api_key }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_goodreads_api_secret">{{_('Goodreads API Secret')}}</label>
|
|
<input type="text" class="form-control" id="config_goodreads_api_secret" name="config_goodreads_api_secret" value="{% if config.config_goodreads_api_secret != None %}{{ config.config_goodreads_api_secret }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_allow_reverse_proxy_header_login" name="config_allow_reverse_proxy_header_login" data-control="reverse-proxy-login-settings" {% if config.config_allow_reverse_proxy_header_login %}checked{% endif %}>
|
|
<label for="config_allow_reverse_proxy_header_login">{{_('Allow Reverse Proxy Authentication')}}</label>
|
|
</div>
|
|
<div data-related="reverse-proxy-login-settings">
|
|
<div class="form-group">
|
|
<label for="config_reverse_proxy_login_header_name">{{_('Reverse Proxy Header Name')}}</label>
|
|
<input type="text" class="form-control" id="config_reverse_proxy_login_header_name" name="config_reverse_proxy_login_header_name" value="{% if config.config_reverse_proxy_login_header_name != None %}{{ config.config_reverse_proxy_login_header_name }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
{% if not config.config_is_initial %}
|
|
{% if feature_support['ldap'] or feature_support['oauth'] %}
|
|
<div class="form-group">
|
|
<label for="config_login_type">{{_('Login type')}}</label>
|
|
<select name="config_login_type" id="config_login_type" class="form-control" data-control="login-settings">
|
|
<option value="0" {% if config.config_login_type == 0 %}selected{% endif %}>{{_('Use Standard Authentication')}}</option>
|
|
{% if feature_support['ldap'] %}
|
|
<option value="1" {% if config.config_login_type == 1 %}selected{% endif %}>{{_('Use LDAP Authentication')}}</option>
|
|
{% endif %}
|
|
{% if feature_support['oauth'] %}
|
|
<option value="2" {% if config.config_login_type == 2 %}selected{% endif %}>{{_('Use OAuth')}}</option>
|
|
{% endif %}
|
|
</select>
|
|
</div>
|
|
{% if feature_support['ldap'] %}
|
|
<div data-related="login-settings-1">
|
|
<div class="form-group">
|
|
<label for="config_ldap_provider_url">{{_('LDAP Server Host Name or IP Address')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_provider_url" name="config_ldap_provider_url" value="{% if config.config_ldap_provider_url != None %}{{ config.config_ldap_provider_url }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_port">{{_('LDAP Server Port')}}</label>
|
|
<input type="number" min="1" max="65535" class="form-control" id="config_ldap_port" name="config_ldap_port" value="{% if config.config_ldap_port != None %}{{ config.config_ldap_port }}{% endif %}" autocomplete="off" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_encryption">{{_('LDAP Encryption')}}</label>
|
|
<label for="config_ldap_encryption">{{_('LDAP Encryption')}}</label>
|
|
<select name="config_ldap_encryption" id="config_ldap_encryption" class="form-control" data-controlall="ldap-cert-settings">
|
|
<option value="0" {% if config.config_ldap_encryption == 0 %}selected{% endif %}>{{ _('None') }}</option>
|
|
<option value="1" {% if config.config_ldap_encryption == 1 %}selected{% endif %}>{{ _('TLS') }}</option>
|
|
<option value="2" {% if config.config_ldap_encryption == 2 %}selected{% endif %}>{{ _('SSL') }}</option>
|
|
</select>
|
|
</div>
|
|
<div data-related="ldap-cert-settings">
|
|
<div class="form-group">
|
|
<label for="config_ldap_cert_path">{{_('LDAP Certificate Path')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_cert_path" name="config_ldap_cert_path" value="{% if config.config_ldap_cert_path != None %}{{ config.config_ldap_cert_path }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="config_ldap_serv_username">{{_('LDAP Administrator Username')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_serv_username" name="config_ldap_serv_username" value="{% if config.config_ldap_serv_username != None %}{{ config.config_ldap_serv_username }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_serv_password">{{_('LDAP Administrator Password')}}</label>
|
|
<input type="password" class="form-control" id="config_ldap_serv_password" name="config_ldap_serv_password" value="" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_dn">{{_('LDAP Distinguished Name (DN)')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_dn" name="config_ldap_dn" value="{% if config.config_ldap_dn != None %}{{ config.config_ldap_dn }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_user_object">{{_('LDAP User Object Filter')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_user_object" name="config_ldap_user_object" value="{% if config.config_ldap_user_object != None %}{{ config.config_ldap_user_object }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="checkbox" id="config_ldap_openldap" name="config_ldap_openldap" {% if config.config_ldap_openldap %}checked{% endif %}>
|
|
<label for="config_ldap_openldap">{{_('LDAP Server is OpenLDAP?')}}</label>
|
|
</div>
|
|
<h4 class="text-center">{{_('Following Settings are Needed For User Import')}}</h4>
|
|
<div class="form-group">
|
|
<label for="config_ldap_group_object_filter">{{_('LDAP Group Object Filter')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_group_object_filter" name="config_ldap_group_object_filter" value="{% if config.config_ldap_group_object_filter != None %}{{ config.config_ldap_group_object_filter }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_group_name">{{_('LDAP Group Name')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_group_name" name="config_ldap_group_name" value="{% if config.config_ldap_group_name != None %}{{ config.config_ldap_group_name }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_ldap_group_members_field">{{_('LDAP Group Members Field')}}</label>
|
|
<input type="text" class="form-control" id="config_ldap_group_members_field" name="config_ldap_group_members_field" value="{% if config.config_ldap_group_members_field != None %}{{ config.config_ldap_group_members_field }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if feature_support['oauth'] %}
|
|
<div data-related="login-settings-2">
|
|
{% for prov in provider %}
|
|
<div class="form-group">
|
|
<a href="{{prov['obtain_link']}}" target="_blank">{{_('Obtain %(provider)s OAuth Credential', provider=prov['provider_name'])}}</a>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_{{ prov['id'] }}_oauth_client_id">{{_('%(provider)s OAuth Client Id', provider=prov['provider_name'])}}</label>
|
|
<input type="text" class="form-control" id="config_{{ prov['id'] }}_oauth_client_id" name="config_{{ prov['id'] }}_oauth_client_id" value="{% if prov['oauth_client_id']%}{{ prov['oauth_client_id'] }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_{{ prov['id'] }}_oauth_client_secret">{{_('%(provider)s OAuth Client Secret', provider=prov['provider_name'])}}</label>
|
|
<input type="text" class="form-control" id="config_{{ prov['id'] }}_oauth_client_secret" name="config_{{ prov['id'] }}_oauth_client_secret" value="{% if prov['oauth_client_secret']%}{{ prov['oauth_client_secret'] }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="panel panel-default">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">
|
|
<a class="accordion-toggle" data-toggle="collapse" href="#collapseeight">
|
|
<span class="glyphicon glyphicon-plus"></span>
|
|
{{_('External binaries')}}
|
|
</a>
|
|
</h4>
|
|
</div>
|
|
<div id="collapseeight" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<div class="form-group">
|
|
<div><input type="radio" name="config_ebookconverter" id="converter0" value="0" {% if config.config_ebookconverter == 0 %}checked{% endif %}>
|
|
<label for="converter0">{{_('No Converter')}}</label></div>
|
|
<div><input type="radio" name="config_ebookconverter" id="converter1" value="1" {% if config.config_ebookconverter == 1 %}checked{% endif %}>
|
|
<label for="converter1">{{_('Use Kindlegen')}}</label></div>
|
|
<div><input type="radio" name="config_ebookconverter" id="converter2" value="2" {% if config.config_ebookconverter == 2 %}checked{% endif %}>
|
|
<label for="converter2">{{_('Use calibre\'s ebook converter')}}</label></div>
|
|
</div>
|
|
<div data-related="calibre">
|
|
<div class="form-group">
|
|
<label for="config_calibre">{{_('E-Book converter settings')}}</label>
|
|
<input type="text" class="form-control" id="config_calibre" name="config_calibre" value="{% if config.config_calibre != None %}{{ config.config_calibre }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="config_calibre">{{_('Path to convertertool')}}</label>
|
|
<input type="text" class="form-control" id="config_converterpath" name="config_converterpath" value="{% if config.config_converterpath != None %}{{ config.config_converterpath }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
{% if rarfile_support %}
|
|
<div class="form-group">
|
|
<label for="config_rarfile_location">{{_('Location of Unrar binary')}}</label>
|
|
<input type="text" class="form-control" name="config_rarfile_location" id="config_rarfile_location" value="{% if config.config_rarfile_location != None %}{{ config.config_rarfile_location }}{% endif %}" autocomplete="off">
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
|
|
<div class="col-sm-12">
|
|
{% if not show_login_button %}
|
|
<button type="submit" name="submit" class="btn btn-default">{{_('Save')}}</button>
|
|
{% endif %}
|
|
{% if show_back_button %}
|
|
<a href="{{ url_for('admin.admin') }}" class="btn btn-default">{{_('Cancel')}}</a>
|
|
{% endif %}
|
|
{% if show_login_button %}
|
|
<a href="{{ url_for('web.login') }}" name="login" class="btn btn-default">{{_('Login')}}</a>
|
|
{% endif %}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|
|
{% block js %}
|
|
<script type="text/javascript">
|
|
$(document).on('change', '#config_use_google_drive', function() {
|
|
$('#config_google_drive_folder').prop('required', $(this).prop('checked'));
|
|
});
|
|
$('.collapse').on('shown.bs.collapse', function(){
|
|
$(this).parent().find(".glyphicon-plus").removeClass("glyphicon-plus").addClass("glyphicon-minus");
|
|
}).on('hidden.bs.collapse', function(){
|
|
$(this).parent().find(".glyphicon-minus").removeClass("glyphicon-minus").addClass("glyphicon-plus");
|
|
});
|
|
</script>
|
|
{% endblock %}
|