如果您想了解自定义插件wordpress更改数据库的相关知识,那么本文是一篇不可错过的文章,我们将对wordpress自定义数据表进行全面详尽的解释,并且为您提供关于Airflow自定义插件,使用da
如果您想了解自定义插件 wordpress 更改数据库的相关知识,那么本文是一篇不可错过的文章,我们将对wordpress自定义数据表进行全面详尽的解释,并且为您提供关于Airflow 自定义插件,使用 datax 抽数、Android gradle 自定义插件、ckeditor 自定义插件,添加的按钮不显示、ckeditor4 自定义插件 多图上传 图片上传的有价值的信息。
本文目录一览:- 自定义插件 wordpress 更改数据库(wordpress自定义数据表)
- Airflow 自定义插件,使用 datax 抽数
- Android gradle 自定义插件
- ckeditor 自定义插件,添加的按钮不显示
- ckeditor4 自定义插件 多图上传 图片上传
自定义插件 wordpress 更改数据库(wordpress自定义数据表)
如何解决自定义插件 wordpress 更改数据库
我创建了一个插件,我需要创建一个新表并更新一个现有的表
我有这个代码,错误数据库没有更新,我可以看到它随着插件版本的更新进入了方法
我已经尝试过这个选项,但它仍然不起作用; Wordpress dbDelta not functioning
如果我在 PHPmyadmin 中运行查询,它会起作用
add_action( ''plugins_loaded'',''plugin_update'' );
function plugin_update() {
$version = get_option( ''my_plugin_version'',''1.0'' );
if ( version_compare( $version,''1.1'' ) < 0 ) {
plugin_updates();
}
}
function plugin_updates() {
global $wpdb;
$table_name = $wpdb->prefix . ''member'';
require_once( ABSPATH . ''wp-admin/includes/upgrade.PHP'' );
$sql = "ALTER TABLE $table_name
MODIFY COLUMN nameMember varchar(255)";
dbDelta( $sql );
update_option( ''my_plugin_version'',''1.1'' );
}
Airflow 自定义插件,使用 datax 抽数
Airflow 自定义插件
Airflow 之所以受欢迎的一个重要因素就是它的插件机制。Python 成熟类库可以很方便的引入各种插件。在我们实际工作中,必然会遇到官方的一些插件不足够满足需求的时候。这时候,我们可以编写自己的插件。不需要你了解内部原理,甚至不需要很熟悉 Python, 反正我连蒙带猜写的。
插件分类
Airflow 的插件分为 Operator 和 Sensor 两种。Operator 是具体要执行的任务插件, Sensor 则是条件传感器,当我需要设定某些依赖的时候可以通过不同的 sensor 来感知条件是否满足。
Airflow 对插件提供的支持
插件肯定是 Python 文件了,系统必然需要加载才能执行。Airflow 提供了一个简单插件管理器,会扫描 $AIRFLOW_HOME/plugins
加载我们的插件。
所以,我们只需要将写好的插件放入这个目录下就可以了。
插件语法
Operator 和 Sensor 都声明了需要的参数,Operator 通过调用 execute 来执行, sensor 通过 poke 来确认。以 Operator 为例子。
插件的使用过程为:
dag -> operator -> hook
Hook 就是任务执行的具体操作了。
Operator 通过继承 BaseOperator 实现对 dag 相关属性的绑定, Hook 通过继承 BaseHook 实现对系统配置和资源获取的一些封装。
自定义一个通知插件 NotifyOperator
前文 https://www.cnblogs.com/woshimrf/p/airflow-dag.html 提到我们通过自定义通知实现多功能任务告警,以下就是一个 demo。
文件结构如下:
plugins
│ ├── hooks
│ └── operators
NotifyOperator
首先,在 operators 目录下创建一个 Operator.
# -*- coding: utf-8 -*-
#
from hooks.notify_hook import NotifyHook
from airflow.operators.bash_operator import BaseOperator
class NotifyOperator(BaseOperator):
"""
使用通知服务发送通知
:param message: 内容
:type message: str or dict
:param receivers: 英文逗号分割的罗盘账号
:type receivers: str
:param subject: 邮件主题
:type subject: str
"""
template_fields = (''message'', ''subject'')
@apply_defaults
def __init__(self,
subject=None,
message=None,
receivers=None,
*args,
**kwargs):
super().__init__(*args, **kwargs)
self.message = message
self.receivers = receivers
self.subject = subject
def execute(self, context):
self.log.info(''Sending notify message. receivers:{} message:{}''.format(self.receivers, self.message))
hook = NotifyHook(
subject=self.subject,
message=self.message,
receivers=self.receivers
)
hook.send()
- 继承 BaseOperator
- 引入 NotifyHook, 这个还没创建,等下创建
- template_fields, 想要使用模板变量替换,比如 {{ds}}, 字段必须声明到 template_fields
- Operator 执行的时候会调用 execute 方法, 这个就是执行的内容
上面可以看出,operator 就是接口声明。
NotifyHook
在 hooks 目录下创建 NotifyHook
# -*- coding: utf-8 -*-
#
import json
import requests
from airflow import AirflowException
from airflow.hooks.http_hook import HttpHook
class NotifyHook(HttpHook):
"""
使用通知服务发送通知
:param send_type: 通知类型选填 MAIL,DINGDING,SMS,选填多个时中间用英文逗号隔开
:type send_type: str
:param message: 内容
:type message: str or dict
:param receivers: 英文逗号分割的账号
:type receivers: str
:param subject: 邮件主题
:type subject: str
"""
def __init__(self,
notify_conn_id=''notify_default'',
send_type=''MAIL'',
subject=None,
message=None,
receivers=None,
*args,
**kwargs
):
super().__init__(http_conn_id=notify_conn_id, *args, **kwargs)
self.send_type = send_type
self.message = message
self.subject = subject
self.receivers = receivers
def _build_message(self):
"""
构建data
"""
data = {
"content": self.message,
"contentType": "HTML",
"receivers": self.receivers,
"sendType": self.send_type,
"sender": ''【Airflow】'',
"subject": ''【Airflow】'' + self.subject
}
return json.dumps(data)
def get_conn(self, headers=None):
"""
Overwrite HttpHook get_conn because just need base_url and headers and
not don''t need generic params
:param headers: additional headers to be passed through as a dictionary
:type headers: dict
"""
self.base_url = ''http://notify.ryan-miao.com''
session = requests.Session()
if headers:
session.headers.update(headers)
return session
def send(self):
"""
Send Notify message
"""
data = self._build_message()
self.log.info(''Sending message: %s'', data)
resp = self.run(endpoint=''/api/v2/notify/send'',
data=data,
headers={''Content-Type'': ''application/json'',
''app-id'': ''ryan'',
''app-key'': ''123456''})
if int(resp.json().get(''retCode'')) != 0:
raise AirflowException(''Send notify message failed, receive error ''
''message %s'', resp.text)
self.log.info(''Success Send notify message'')
- 这里使用的我自己的通知服务 api 调用。因为是 http 请求,所以直接继承 HttpHook 来发送请求就可以了。
- http_conn_id 是用来读取数据库中 connection 里配置的 host 的,这里直接覆盖,固定我们通知服务的地址。
- 通过抛出异常的方式来终止服务
如何使用
将上面两个文件放到 airflow 对应的 plugins 目录下, airflow 就自动加载了。然后,当做任务类型使用
from operators.notify_operator import NotifyOperator
notification = NotifyOperator(
task_id="we_are_done",
subject=''发送邮件'',
message=''content'',
receivers=''ryanmiao''
)
也可以直接执行。比如,我们前面提到任务失败告警可以自定义通知。
from operators.notify_operator import NotifyOperator
def mail_failure_callback(receivers):
"""
失败后邮件通知
:receivers 接收人,多个接收人用英文逗号分开
"""
def mail_back(context):
subject="【执行失败】DAG {} TASK {} ds {}".format(
context[''task_instance''].dag_id,
context[''task_instance''].task_id,
context[''ds''])
message="【执行失败】DAG: {};<br\> TASK: {} <br\>; ds {} <br\>; 原因: {} .<br\>" \
"查看地址: http://airflow.ryan-miao.com/admin/airflow/tree?dag_id={}" \
.format(
context[''task_instance''].dag_id,
context[''task_instance''].task_id,
context[''ds''],
context[''exception''],
context[''task_instance''].dag_id)
return NotifyOperator(
task_id="mail_failed_notify_callback",
subject=subject,
message=message,
receivers=receivers
).execute(context)
return mail_back
default_args = {
''owner'': ''ryanmiao'',
''depends_on_past'': False,
''start_date'': datetime(2019, 5, 1, 9),
''on_failure_callback'': mail_failure_callback(receivers=''ryanmiao''),
''retries'': 0
}
dag = DAG(
''example'', default_args=default_args, schedule_interval=None)
自定义一个 RDBMS2Hive 插件
我们任务调度有个常见的服务是数据抽取到 Hive,现在来制作这个插件,可以从关系数据库中读取数据,然后存储到 hive。这样,用户只要在 airflow 配置一下要抽数的 database, table 和目标 hive table 就可以实现每天数据入库了。
异构数据传输转换工具很多, 最简单的就是使用原生的 dump 工具,将数据 dump 下来,然后 import 到另一个数据库里。
比如 postgres dump
将 ${sql}
查询的列导出到文件 ${export_data_file}
psql -h$SRC_HOST_IP -U$SRC_USER_NAME -d$SRC_DB -p$SRC_HOST_PORT -c "\copy (${sql}) to ''${export_data_file}'' WITH NULL AS ''''"
然后导入 hive
LOAD DATA LOCAL INPATH ''${export_data_file}'' INTO TABLE $TAR_TABLENAME PARTITION (BIZDATE=''$BIZ_DATE'')
对 postgres 来说,copy 是最快的方案了, 但可能会遇到 \t
,\n
等各种转义符号,导出的 txt 文件或者 cvs 文件格式就会混乱,需要做对应符号转义处理。
同样, mysql 可以直接把数据查询出来
cat search.sql | mysql -h"$SRC_HOST_IP" -u"$SRC_USER_NAME" -p"$SRC_USER_PWD" -P"$SRC_HOST_PORT" -D"$SRC_DB" --default-character-set=${mysql_charset} -N -s | sed "s/NULL/\\\\N/ig;s/\\\\\\\\n//ig" > result.txt
上述这些命令行的好处就是快,不好的地方在于 shell 命令的脆弱性和错误处理。最终,选择了集成化的数据转换工具 datax. datax 是阿里巴巴开源的一款异构数据源同步工具, 虽然看起来不怎么更新了,但简单使用还是可以的。https://github.com/alibaba/DataX
datax 的用法相对简单,按照文档配置一下读取数据源和目标数据源,然后执行调用就可以了。可以当做命令行工具来使用。
结合 airflow,可以自己实现 datax 插件。通过读取 connections 拿到数据源链接配置,然后生成 datax 的配置文件 json,最后调用 datax 执行。下面是一个从 pg 或者 mysql 读取数据,导入 hive 的插件实现。
主要思路是:
- hdfs 创建一个目录
- 生成 datax 配置文件
- datax 执行配置文件,将数据抽取到 hdfs
- hive 命令行 load hdfs
RDBMS2HiveOperator
# -*- coding: utf-8 -*-
#
#
"""
postgres或者mysql 入库到hdfs
"""
import os
import signal
from hooks.rdbms_to_hive_hook import RDBMS2HiveHook
from airflow.exceptions import AirflowException
from airflow.models import BaseOperator
class RDBMS2HiveOperator(BaseOperator):
"""
传输pg到hive
https://github.com/alibaba/DataX
:param conn_id: pg连接id
:param query_sql : pg查询语句
:param split_pk : pg分割主键, NONE表示不分割,指定后可以多线程分割,加快传输
:param hive_db : hive的db
:param hive_table: hive的table
:param hive_table_column column数组, column={name:a, type: int} 或者逗号分割的字符串, column=a,b,c
:param hive_table_partition 分区bizdate值
"""
template_fields = (''query_sql'', ''hive_db'', ''hive_table'',''hive_table_partition'')
ui_color = ''#edd5f1''
@apply_defaults
def __init__(self,
conn_id,
query_sql,
hive_db,
hive_table,
hive_table_column,
hive_table_partition,
split_pk=None,
*args,
**kwargs):
super().__init__(*args, **kwargs)
self.conn_id = conn_id
self.query_sql = query_sql
self.split_pk = split_pk
self.hive_db = hive_db
self.hive_table = hive_table
self.hive_table_column = hive_table_column
self.hive_table_partition = hive_table_partition
def execute(self, context):
"""
Execute
"""
task_id = context[''task_instance''].dag_id + "#" + context[''task_instance''].task_id
self.hook = RDBMS2HiveHook(
task_id = task_id,
conn_id = self.conn_id,
query_sql = self.query_sql,
split_pk=self.split_pk,
hive_db=self.hive_db,
hive_table=self.hive_table,
hive_table_column=self.hive_table_column,
hive_table_partition=self.hive_table_partition
)
self.hook.execute(context=context)
def on_kill(self):
self.log.info(''Sending SIGTERM signal to bash process group'')
os.killpg(os.getpgid(self.hook.sp.pid), signal.SIGTERM)
RDBMS2HiveHook
# -*- coding: utf-8 -*-
#
"""
datax入库hive
"""
import subprocess
import uuid
import json
import os
from airflow.exceptions import AirflowException
from airflow.hooks.base_hook import BaseHook
class RDBMS2HiveHook(BaseHook):
"""
Datax执行器
"""
def __init__(self,
task_id,
conn_id,
query_sql,
hive_db,
hive_table,
hive_table_column,
hive_table_partition,
split_pk=None):
self.task_id = task_id
self.conn = self.get_connection(conn_id)
self.query_sql = query_sql
self.split_pk = split_pk
self.hive_db = hive_db
self.hive_table = hive_table
self.hive_table_partition = hive_table_partition
self.log.info("Using connection to: {}:{}/{}".format(self.conn.host, self.conn.port, self.conn.schema))
self.hive_table_column = hive_table_column
if isinstance(hive_table_column, str):
self.hive_table_column = []
cl = hive_table_column.split('','')
for item in cl:
hive_table_column_item = {
"name": item,
"type": "string"
}
self.hive_table_column.append(hive_table_column_item)
def Popen(self, cmd, **kwargs):
"""
Remote Popen
:param cmd: command to remotely execute
:param kwargs: extra arguments to Popen (see subprocess.Popen)
:return: handle to subprocess
"""
self.sp = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
**kwargs)
for line in iter(self.sp.stdout):
self.log.info(line.strip().decode(''utf-8''))
self.sp.wait()
self.log.info("Command exited with return code %s", self.sp.returncode)
if self.sp.returncode:
raise AirflowException("Execute command failed")
def generate_setting(self):
"""
datax速度等设置
"""
self.setting= {
"speed": {
"byte": 104857600
},
"errorLimit": {
"record": 0,
"percentage": 0.02
}
}
return self.setting
def generate_reader(self):
"""
datax reader
"""
conn_type = ''mysql''
reader_name = ''mysqlreader''
if(self.conn.conn_type == ''postgres''):
conn_type = ''postgresql''
reader_name = ''postgresqlreader''
self.jdbcUrl = "jdbc:"+conn_type+"://"+self.conn.host.strip()+":"+str(self.conn.port)+"/"+ self.conn.schema.strip()
self.reader = {
"name": reader_name,
"parameter": {
"username": self.conn.login.strip(),
"password": self.conn.password.strip(),
"connection": [
{
"querySql": [
self.query_sql
],
"jdbcUrl": [
self.jdbcUrl
]
}
]
}
}
return self.reader
def generate_writer(self):
"""
datax hdafs writer
"""
self.file_type = "text"
self.hdfs_path = "/datax/"+self.hive_db+"/"+self.hive_table+"/"+self.hive_table_partition
self.log.info("临时存储目录:{}".format(self.hdfs_path))
self.writer = {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://nameservice1",
"hadoopConfig": {
"dfs.nameservices": "nameservice1",
"dfs.ha.automatic-failover.enabled.nameservice1": True,
"ha.zookeeper.quorum": "bigdata2-prod-nn01.ryan-miao.com:2181,bigdata2-prod-nn02.ryan-miao.com:2181,bigdata2-prod-nn03.ryan-miao.com:2181",
"dfs.ha.namenodes.nameservice1": "namenode117,namenode124",
"dfs.namenode.rpc-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:8020",
"dfs.namenode.servicerpc-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:8022",
"dfs.namenode.http-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:50070",
"dfs.namenode.https-address.nameservice1.namenode117": "bigdata2-prod-nn01.ryan-miao.com:50470",
"dfs.namenode.rpc-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:8020",
"dfs.namenode.servicerpc-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:8022",
"dfs.namenode.http-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:50070",
"dfs.namenode.https-address.nameservice1.namenode124": "bigdata2-prod-nn02.ryan-miao.com:50470",
"dfs.replication": 3,
"dfs.client.failover.proxy.provider.nameservice1": "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider"
},
"fileType": self.file_type,
"path": self.hdfs_path,
"fileName": self.task_id,
"column": self.hive_table_column,
"writeMode": "nonConflict",
"fieldDelimiter": "\t"
}
}
return self.writer
def generate_config(self):
content = [{
"reader": self.generate_reader(),
"writer": self.generate_writer()
}]
job = {
"setting": self.generate_setting(),
"content": content
}
config = {
"job": job
}
self.target_json = json.dumps(config)
# write json to file
self.json_file= ''/tmp/datax_json_''+self.task_id+ uuid.uuid1().hex
# 打开一个文件
fo = open(self.json_file, "w")
fo.write(self.target_json)
fo.close()
self.log.info("write config json {}".format(self.json_file))
return self.json_file
def execute(self, context):
self.generate_config()
# check hdfs_path
hdfs_path = self.hdfs_path
if(not hdfs_path.startswith(''/datax/'')):
raise AirflowException("hdfs路径填写错误,不在/datax目录下")
# 创建目录
cmd = [''hadoop'', ''fs'', ''-mkdir'', ''-p'', hdfs_path]
self.Popen(cmd)
# 删除文件
if(not hdfs_path.startswith(''/datax/'')):
raise AirflowException("hdfs路径填写错误,不在/datax目录下")
files_path = hdfs_path+"/*";
try:
cmd = [''hadoop'', ''fs'', ''-rm'', files_path]
self.Popen(cmd)
except Exception:
self.log.info(''ignore err, just make sure the dir is clean'')
pass
# 上传文件
datax_home = ''/data/opt/datax/bin''
cmd = [ ''python'', datax_home + ''/datax.py'', self.json_file]
self.Popen(cmd)
# 删除配置文件
os.remove(self.json_file)
# hive加载
#hive load data from hdfs
hql = "LOAD DATA INPATH ''"+ hdfs_path + "'' OVERWRITE INTO TABLE " \
+ self.hive_db+"."+self.hive_table + " PARTITION (bizdate="+ self.hive_table_partition +")"
cmd = [''hive'', ''-e'', "\"" + hql + "\""]
self.Popen(cmd)
如何使用
- admin 登录 airflow
- 配置 connection, 配置 pg 或者 mysql 的数据库
- 修改 hdfs 集群配置信息
- 创建一个 DAG
from airflow import DAG
from operators.rdbms_to_hive_operator import RDBMS2HiveOperator
from datetime import datetime, timedelta
from dag_utils import compass_utils
default_args = {
''owner'': ''ryanmiao'',
''depends_on_past'': False,
''start_date'': datetime(2019, 5, 1, 9),
''on_failure_callback'': compass_utils.failure_callback(dingding_conn_id=''dingding_bigdata'', receivers=''ryanmiao''),
# ''on_success_callback'': compass_utils.success_callback(dingding_conn_id=''dingding_bigdata'', receivers=''ryanmiao''),
''retries'': 0
}
dag = DAG(
''example_pg2hive'', default_args=default_args, schedule_interval=None)
# CREATE TABLE test.pg2hive_test(
# ftime int,
# raw_cp_count int,
# raw_to_delete_cp_count bigint,
# create_time timestamp
# )
# COMMENT ''这个是测试datax表''
# PARTITIONED BY (bizdate int)
# ROW FORMAT DELIMITED
# FIELDS TERMINATED BY ''\t''
# LINES TERMINATED BY ''\n''
# STORED AS TEXTFILE;
hive_table_column = "ftime,raw_cp_count,raw_to_delete_cp_count,create_time"
t1 = RDBMS2HiveOperator(
task_id=''pg2hive'',
conn_id=''pg_rdb_poi'',
query_sql=''select ftime, raw_cp_count, raw_to_delete_cp_count, create_time from tbl_poi_report limit 1000'',
hive_db=''test'',
hive_table=''pg2hive_test'',
hive_table_column=hive_table_column,
hive_table_partition="{{ ds_nodash }}",
dag=dag
)
原文出处:https://www.cnblogs.com/woshimrf/p/airflow-plugin.html
Android gradle 自定义插件
Gradle 的插件有三种打包方式:
- 构建脚本:插件逻辑写在 build.gradle 中,适用于逻辑简单的任务,但是该方式实现的插件在该构建脚本之外是不可见的,只能用于当前脚本。
-
buildSrc项目:根据语言插件代码放在 rootProjectDir/buildSrc/src/main/groovy 目录中或者 rootProjectDir/buildSrc/src/main/java 或者rootProjectDir/buildSrc/src/main/kotlin,该插件对于该项目中构建使用的每个构建脚本都是可见的,适用于逻辑复杂但又不需要对外可见的插件
- 独立项目:一个独立的 Groovy/Java/Kotlin 项目,该项目生成并发布一个 jar 然后可以在多个版本中使用它并与他人共享,并且此 jar 包可能包含一些插件,或者将多个相关的任务类捆绑到单个库中
接下来我们先从基本的构建脚本中的插件开始
1、构建脚本:
创建 Gradle 插件,需要创建一个实现 Plugin 接口的类,当插件应用于项目时,Gradle 会创建插件类的实例并调用实例的 apply() 方法,如果需要的话,插件还可以用于配置项目
把插件变得可配置,添加一些简单扩展属性到 project 上,比如可以添加一个 greeting 扩展对象到 project 上,这就允许我们去配置 greeting 对象。
文件是 build.gradle:
class GreetingPluginExtension { String message = ''Hello from GreetingPlugin'' } class GreetingPlugin implements Plugin<Project> { void apply(Project project) { // Add the ''greeting'' extension object def extension = project.extensions.create(''greeting'',GreetingPluginExtension) Add a task that uses configuration from the extension object project.task(''hello'') { doLast { println extension.message } } } } apply plugin: GreetingPlugin Configure the extension greeting.message = ''Hi from Gradle''
2、独立项目
创建Gradle Module
AndroidStudio中是没有新建类似Gradle Plugin这样的选项的,那我们如何在AndroidStudio中编写Gradle插件,并打包出来呢?
-
首先,你得新建一个Android Project
-
然后再新建一个 Module,这个 Module用于开发 Gradle 插件,同样,Module 里面没有 gradle plugin 给你选,但是我们只是需要一个“容器”来容纳我们写的插件,因此,你可以随便选择一个 Module 类型(如 Phone & Tablet Module 或 Android Librarty ),因为接下来一步我们是将里面的大部分内容删除,所以选择哪个类型的 Module 不重要。
-
将 Module 里面的内容删除,只保留 build.gradle 文件和 src/main 目录。
-
由于 gradle 是基于 groovy,因此,我们开发的 gradle 插件相当于一个 groovy 项目。所以需要在 main 目录下新建 groovy 目录
-
groovy 又是基于Java,因此,接下来创建groovy的过程跟创建java很类似。在 groovy 新建包名,如:com.sjq.greetplugin,然后在该包下新建groovy文件,通过 new->file→GreetingPlugin.groovy 来新建名为 GreetingPlugin 的 groovy 文件。一定是要有.groovy 后缀文件。
-
为了让我们的 groovy 类申明为 gradle 的插件,新建的 groovy 需要实现 org.gradle.api.Plugin 接口。
如下所示:
package com.sjq.greetplugin 包名一定要有,不然打包不会打进对应包里去,很可能就会出现类找不到的情况。 import org.gradle.api.Plugin org.gradle.api.Project { @Override apply(Project project) { project.task("publishPluginTest"){ task 名字 doLast { println("新内容是:null") } } } }
定义好了自己的 gradle 插件类,接下来就是告诉 gradle,哪一个是我们自定义的插件类,因此,需要在 main 目录下新建 resources 目录,然后在resources 目录里面再新建 meta-inf 目录,再在 meta-inf 里面新建 gradle-plugins 目录。最后在 gradle-plugins 目录里面新建 properties 文件,注意这个文件的命名,你可以随意取名,但是后面使用这个插件的时候,会用到这个名字。比如,你取名为 com.sjq.gradle.properties,而在其他build.gradle 文件中使用自定义的插件时候则需写成:
apply plugin: ''com.sjq.greetplugin''
然后在com.hc.gradle.properties
文件里面指明你自定义的类
implementation-class=com.sjq.greetplugin.GreetingPlugin 注意后面跟的是插件名字,注意大小写
此处我们还是采用包名,方便记忆。
到这里后,整个目录结果如下图所示:
因为我们要用到 groovy
以及后面打包要用到 maven
,所以在我们自定义的 Module
下的 build.gradle
需要添加如下代码:
apply plugin: ''groovy'' apply plugin: ''maven'' dependencies { gradle sdk compile gradleApi() groovy sdk compile localGroovy() } repositories { mavenCentral() }
打包到本地Maven
前面我们已经自定义好了插件,接下来就是要打包到 Maven 库里面去了,你可以选择打包到本地,或者是远程服务器中。在我们自定义 Module
目录下的 build.gradle 添加如下代码:
group和version在后面使用自定义插件的时候会用到 group=''com.hc.plugin'' version=''1.0.0'' uploadArchives { repositories { mavendeployer { 提交到远程服务器: repository(url: "http://www.xxx.com/repos") { authentication(userName: "admin",password: "admin") } 本地的Maven地址设置为../repos repository(url: uri(''../repos'')) } } }
其中,group
和 version
后面会用到,我们后面再讲。虽然我们已经定义好了打包地址以及打包相关配置,但是还需要我们让这个打包 task 执行。点击 AndroidStudio
右侧的 gradle 工具
,如下图所示:
可以看到有 uploadArchives
这个 Task,
双击 uploadArchives
就会执行打包上传啦!执行完成后,去我们的 Maven
本地仓库查看一下:
其中,com/sjq/greetplugin
这几层目录是由我们的 group
指定,myplugin
是模块的名称,1.0.0
是版本号( version
指定)。
使用自定义的插件
接下来就是使用自定义的插件了,一般就是在 app 这个模块中使用自定义插件,因此在 app 这个 Module 的 build.gradle 文件中,需要指定本地 Maven 地址、自定义插件的名称以及依赖包名。简而言之,就是在 app 这个 Module 的 build.gradle 文件中后面附加如下代码:
buildscript { repositories { maven {本地Maven仓库地址 url uri(''../repos'') } } dependencies { 格式为-->group:module:version classpath ''com.sjq.greetplugin:GreetingPlugin:1.0.0'' } } com.sjq.greetplugin 为 resources/meta-inf/gradle-plugins 下的 properties 文件名称 apply plugin: ''com.sjq.greetplugin''
在当前目录下输入命令行:
./gradlew -q publishPluginTest
运行结果:
3、开发只针对当前项目的Gradle插件
前面我们讲了如何自定义 gradle 插件并且打包出去,可能步骤比较多。有时候,你可能并不需要打包出去,只是在这一个项目中使用而已,那么你无需打包这个过程。
只是针对当前项目开发的 Gradle 插件相对较简单。步骤之前所提到的很类似,只是有几点需要注意:
新建的 Module 名称必须为 BuildSrc,无需 resources 目录
其中,build.gradle
内容为:
依赖groovy插件 plugins { id ''groovy'' } dependencies { implementation gradleApi() implementation localGroovy() }
CustomPluginB 内容是
package com.sjq.happy.myplugin 在单独的groovy文件中定义插件类 class CustomPluginB { @Override apply(Project project) { project.task(''CustomPluginTaskB'') { doFirst { println ''This is custom plugin TaskB'' } } } }
在app
这个Module
中如何使用呢?直接在app的build.gradle下加入
import com.sjq.happy.myplugin.CustomPluginB
在输入命令行:
./gradlew -q CustomPluginTaskB
结果如下图所示:
总结
以上是小编为你收集整理的Android gradle 自定义插件全部内容。
如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
ckeditor 自定义插件,添加的按钮不显示
plugins目录下:
上面这个是我自己照着官网案例写的,
plugin.js内容:
CKEDITOR.plugins.add(''timestamp'', {
icons: ''vi'',
hidpi:true,
init: function (editor) {
editor.addCommand(''insertTimestamp'', {
exec: function (editor) {
editor.insertHtml(''hello world'');
}
});
editor.ui.addButton(''Timestamp'',{
label:''Timestamp'',
command:''insertTimestamp'',
toolbar:''insert,21''
})
}
});
这个是打开的效果,红框那里确实是有我添加的按钮存在,就是不显示图标:
然后我在控制台看到的下面图上第一个红框是我在官网上下载flash插件,这个是正常,可以看到style样式中有图标路径,第二个红框就是我自己添加的一个按钮,那里只有sytle中并没有图标的路径:
我对照flash插件,然后把falsh中的其他部分都注释掉,最后flash和我自己写的插件中的plugin.js中的一样了,但是还是和上面的情况一样,这是个什么情况。。。。。。。。求jiao
ckeditor4 自定义插件 多图上传 图片上传
参考资料:整合ueditor多图 http://blog.csdn.net/shunlongjin/article/details/24266665
自定义插件:http://www.cnblogs.com/lts8989/archive/2011/08/04/2127326.html
基础配置:http://blog.csdn.net/itmyhome1990/article/details/17264627
关于自定义插件 wordpress 更改数据库和wordpress自定义数据表的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于Airflow 自定义插件,使用 datax 抽数、Android gradle 自定义插件、ckeditor 自定义插件,添加的按钮不显示、ckeditor4 自定义插件 多图上传 图片上传等相关内容,可以在本站寻找。
本文标签: